[
  {
    "path": ".github/workflows/build.yml",
    "content": "on:\r\n  # Triggers the workflow on push or pull request events but only for the main branch\r\n  push:\r\n    branches: [ main ]\r\n    tags:\r\n      - '*'\r\n\r\n#  pull_request:\r\n#    branches: [ main ]\r\n\r\n  # Allows you to run this workflow manually from the Actions tab\r\n  workflow_dispatch:\r\n\r\njobs:\r\n  release:\r\n    name: Create new releas and attach IPKs\r\n    runs-on: ubuntu-latest\r\n    permissions:\r\n      contents: write\r\n    needs: build\r\n    steps:\r\n      - name: Branch name\r\n        id: branch_name\r\n        run: |\r\n          echo ::set-output name=IS_TAG::${{ startsWith(github.ref, 'refs/tags/') }}\r\n          echo ::set-output name=SOURCE_TAG::${GITHUB_REF#refs/tags/}\r\n          echo ::set-output name=RELEASE_VERSION::${GITHUB_REF#refs/*/}\r\n\r\n      - name: Download artifact\r\n        env:\r\n            IS_TAG: ${{ steps.branch_name.outputs.IS_TAG }}\r\n            SOURCE_TAG: ${{ steps.branch_name.outputs.SOURCE_TAG }}\r\n        if: \"${{ env.IS_TAG == 'true' }}\"\r\n        uses: actions/download-artifact@v4\r\n        with:\r\n          name: ${{ env.ARTIFACT_NAME }}\r\n\r\n      - name: View content\r\n        env:\r\n            IS_TAG: ${{ steps.branch_name.outputs.IS_TAG }}\r\n            SOURCE_TAG: ${{ steps.branch_name.outputs.SOURCE_TAG }}\r\n        if: \"${{ env.IS_TAG == 'true' }}\"\r\n        run: ls -R\r\n\r\n      - name: Create tagged release\r\n        id: create_release\r\n        env:\r\n            IS_TAG: ${{ steps.branch_name.outputs.IS_TAG }}\r\n            SOURCE_TAG: ${{ steps.branch_name.outputs.SOURCE_TAG }}\r\n        if: \"${{ env.IS_TAG == 'true' }}\"\r\n        uses: ncipollo/release-action@v1\r\n        with:\r\n          tag: ${{ env.SOURCE_TAG }}\r\n          name: ${{ env.SOURCE_TAG }}\r\n          token: ${{ secrets.GITHUB_TOKEN }}\r\n          prerelease: false\r\n          artifacts: \"./*/*.ipk\"\r\n\r\n  build:\r\n    name: Build .ipk packages\r\n    runs-on: ubuntu-latest\r\n\r\n    steps:\r\n      - name: Checkout Code\r\n        uses: actions/checkout@v2\r\n\r\n      - name: Get GitHub Build Number (ENV)\r\n        id: get_buildno\r\n        run: echo \"GITHUBBUILDNUMBER=${{ github.run_number }}\" >> $GITHUB_ENV\r\n        continue-on-error: true\r\n\r\n      - name: Get repository name\r\n        run: echo \"REPOSITORY_NAME=$(echo '${{ github.repository }}' | cut -d '/' -f2)\" >> $GITHUB_ENV\r\n        shell: bash\r\n\r\n      - name: Make artifact name\r\n        id: make_artifactname\r\n        run: |\r\n          ARTIFACT_NAME=\"${{ env.REPOSITORY_NAME }}-${{ github.run_number }}\"\r\n          echo \"${ARTIFACT_NAME}\"\r\n          echo \"ARTIFACT_NAME=${ARTIFACT_NAME}\" >> $GITHUB_ENV\r\n\r\n      - uses: nttld/setup-ndk@v1\r\n        id: setup-ndk\r\n        with:\r\n          ndk-version: r23b\r\n\r\n      - name: Build .ipk\r\n        env:\r\n          ANDROID_NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }}\r\n        run: |\r\n          export PATH=$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin/:$PATH\r\n          make ipk\r\n\r\n      - name: Upload release artifacts\r\n        uses: actions/upload-artifact@v4\r\n        with:\r\n          name: ${{ env.ARTIFACT_NAME }}\r\n          path: |\r\n            ./ipk/*.ipk\r\n"
  },
  {
    "path": ".gitignore",
    "content": "*.o\nosd_dji\nmsp_displayport_mux\nipk/goggle/build\nipk/airunit/build\n*.ipk\nrepo\nobj\nlibs"
  },
  {
    "path": "FAKEHD.md",
    "content": "# FakeHD\n\nBetaflight's (before 4.4) OSD supports a 30 * 16 Grid, which looks large/blocky when displayed in the DJI Goggles.\n\nFor versions of Betaflight before 4.4 (or other FC firmwares without HD support), as a workaround, we have a mode called \"FakeHD\". It chops up the OSD into sections and positions them evenly around an HD grid (with gaps between) - the way this is done is configurable, explained below. This then allows the use of smaller fonts - so it looks nicer/more in keeping with the built in Goggles OSD (but you still only have 30 columns / 16 rows to configure.... and you have the gaps to contend with).\n\nA diagram to help...\n\n| Before (in Configurator)                               | After (in Goggles)                                  |\n| ------------------------------------------------------ | --------------------------------------------------- |\n| ![FakeHD Before](/docs/img/fakehd_before.png \"Before\") | ![FakeHD After](/docs/img/fakehd_after.png \"After\") |\n\n##### To configure/enable:\n\nVisit https://fpv.wtf/package/fpv-wtf/msp-osd with your goggles connected, and check \"Fake HD\"\n\nOptionally, place custom fonts in the root of your sd card, using the names `font_bf_hd.png`\n\nConfiguration of the grid is also possible; see below.\n\nNo air unit/vista config is required.\n\n##### Menu Switching - Getting rid of gaps when displaying Menu / Post Flight Stats + displaying centered:\n\nIn order to have menus (accessible in Betaflight using stick commands) and post-flight stats appear in the center of the screen while using FakeHD, rather than having gaps + looking broken, you should set up menu switching.\n\nFakeHD can use the presence/absence of a character in the OSD as a switch to indicate when you are in regular OSD mode or in the menu/stats and switch to centering temporarily when needed.\n\nBy default, the `Throttle Position` icon is used (character 4) - but you can set any character you want. It needs to be something that doesn't flash or change in the regular OSD, and ideally (but not essential) something that is never in the menu/post flight stats. The icons next to various elements are obvious choices here. You can set this using the `fakehd_menu_switch` configuration parameter.\n\nBetaflight has a list here: https://github.com/betaflight/betaflight/blob/master/docs/osd.md\n\n\nIf you want to use FakeHD with some other Flight Controller, you will need to find an appropriate icon. (Let us know - we can include the information here).\n\nFinally, if you don't have anything in your OSD that works for menu switching, you can hide the menu switching character and the subsequent 5 characters, allowing you to add the `Throttle Position` element but not have to see it on screen. This is enabled by setting `fakehd_hide_menu_switch` to true.\n\nNotes:\n\n - Because of this switching feature, if you change to a different quad or OSD config (specifically the switch element is in a different place), FakeHD will center - you will need to reboot your Goggles to get it to recognise the switch element in a different location.\n\n - Also because of this switching, if you are editing OSD in the configurator with the goggles on to preview and you move the switching element around, it will cause the gaps to be disabled and everything to center. The new location of the switching element will be found next time you reboot the goggles and it'll work as normal.\n\n##### I don't want gaps at all...\n\nSet config `fakehd_lock_center` to true and the center locking used for the menu / post flight stats will be enabled permanently (ie: you fly with it as well) - basically it places your 30 x 16 SD grid into the middle of an HD grid, there's never any gaps - see diagram below + compare to diagrams above.\n\n| After/Centered (in Goggles) `fakehd_lock_center`                               |\n| ------------------------------------------------------------------------------ |\n| <img src=\"/docs/img/fakehd_centered.png\" alt=\"After / Centered\"  height=200 /> |\n\n##### Customising the default FakeHD grid.\n\nBy default, FakeHD positions your SD grid into the HD grid as per the before/after diagram above.\n\nIf this doesn't work for you for whatever reason, some customisation is available. It's necessarily a little complicated.\n\nEach row can be set to one of:\n\n| Code | Description                                                                                                                    |\n| ---- | ------------------------------------------------------------------------------------------------------------------------------ |\n| L    | Left aligned, the SD row occupies cell 1-30, no gaps                                                                           |\n| C    | Center aligned, the SD row occupies cell 16-45, no gaps                                                                        |\n| R    | Right aligned, , the SD row occupies cell 31-60, no gaps                                                                       |\n| W    | Split A - Row is split in 3, the FakeHD default, filling cells 1-10, 26-35, 51-60                                              |\n| T    | Split B - Row is split in 2, touching the sides - filling cells 1-15 + 46-60                                                   |\n| F    | Split C - Row is split in 2 and away from the sides - filling cells 11-25 + 36-50                                              |\n| D    | DJI Special - Row is centered but pushed a little left; used to posiution the bottom row between the existing DJI OSD elements |\n\n<img src=\"/docs/img/fakehd_rows.png\" alt=\"Columns\"  height=200 />\n\nAnd then the columns as a whole can be set to one of:\n\n| Code | Description                                                                 |\n| ---- | --------------------------------------------------------------------------- |\n| T    | Top aligned, OSD occupies rows 1-16                                         |\n| M    | Center aligned, OSD occupies cells 4-19, no gaps                            |\n| B    | Bottom aligned, , the OSD occupies rows 7-22                                |\n| S    | Split - FakeHD default - split in 3, OSD occupies rows 1 - 5, 9 - 13, 17-22 |\n\nUsing the default row config; here's what they all look like:\n\n| T                                                    | M                                                    | B                                                    | S                                                |\n| ---------------------------------------------------- | ---------------------------------------------------- | ---------------------------------------------------- | ------------------------------------------------ |\n| <img src=\"/docs/img/fakehd_columns_t.png\" alt=\"T\" /> | <img src=\"/docs/img/fakehd_columns_m.png\" alt=\"M\" /> | <img src=\"/docs/img/fakehd_columns_b.png\" alt=\"B\" /> | <img src=\"/docs/img/fakehd_after.png\" alt=\"S\" /> |\n\n###### To configure rows\n\nRows config accepts a 16 character long string; each character configuring it's corresponding row. The default FakeHD config would be set like this:\n\n`fakehd_rows = WWWWWWCCWWWWWWWD`\n\nThe characters are case sensitive, but the configurator will reject invalid characters.\n\n###### To configure columns\n\nColumns accepts a single character configuring how the whole grid is aligned. The default would be set like this:\n\n`fakehd_columns = S`\n\nThe characters are case sensitive, but the configurator will reject invalid characters.\n\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": "Makefile.dji",
    "content": "CC=armv7a-linux-androideabi19-clang\nCFLAGS=-I. -O2\nLIB_SHIMS = libshims/libduml_hal.so\n\n.PHONY: repo\n\n%.o: %.c $(DEPS)\n\t$(CC) -c -o $@ $< $(CFLAGS)\n\n#this doesn't work by default\n#an extra duss_result_t(frame_pop_handler)\n#is generated that the compiler doesn't like\nlibshims/%.c: %.h\n\tstubgen -g -e .c -l -N -t libshims -p \"../\" -n $<\n\nlibshims/lib%.so: libshims/%.c\n\t$(CC) -Wno-c2x-extensions -O2 -shared -Wl,-soname,libduml_hal.so -o $@ $< $(CFLAGS)\n\nlibshims: $(LIB_SHIMS)\n\nall: jni/*\n\tndk-build\n\ninstall: all\n\tinstall -d ipk/goggle/build/data/opt/fonts/\n\tinstall fonts/*.png ipk/goggle/build/data/opt/fonts/\n\tinstall -d ipk/goggle/build/data/opt/mspdictionaries/\n\tinstall dictionaries/*.bin ipk/goggle/build/data/opt/mspdictionaries/\n\tinstall -d ipk/goggle/build/data/opt/etc/preload.d/\n\tinstall libs/armeabi-v7a/libdisplayport_osd_shim.so ipk/goggle/build/data/opt/etc/preload.d/\n\tinstall -d ipk/airunit/build/data/opt/bin/\n\tinstall libs/armeabi-v7a/msp_displayport_mux ipk/airunit/build/data/opt/bin/\n\tinstall -d ipk/airunit/build/data/opt/mspdictionaries/\n\tinstall dictionaries/*.bin ipk/airunit/build/data/opt/mspdictionaries/\n\tinstall -d ipk/airunit/build/data/opt/etc/package-config/msp-osd/\n\tinstall config/airunit/* ipk/airunit/build/data/opt/etc/package-config/msp-osd/\n\tinstall -d ipk/goggle/build/data/opt/etc/package-config/msp-osd/\n\tinstall config/goggles/* ipk/goggle/build/data/opt/etc/package-config/msp-osd/\n\ngoggle_ipk: install\n\t$(eval PKG_NAME := $(shell cat ./ipk/goggle/control/control | grep Package | cut -d\" \" -f2))\n\t$(eval ARCH := $(shell cat ./ipk/goggle/control/control | grep Architecture | cut -d\" \" -f2))\n\t$(eval VERSION :=$(shell cat ./ipk/goggle/control/control | grep Version | cut -d\" \" -f2))\n\t$(eval IPK_NAME := \"${PKG_NAME}_${VERSION}_${ARCH}.ipk\")\n\tcp -r ipk/goggle/data ipk/goggle/build/\n\techo \"2.0\" > ipk/goggle/build/debian-binary\n\tcp -r ipk/goggle/control ipk/goggle/build/\n\tcd ipk/goggle/build/control && tar czvf ../control.tar.gz .\n\tcd ipk/goggle/build/data && tar czvf ../data.tar.gz .\n\tcd ipk/goggle/build && tar czvf \"../../${IPK_NAME}\" ./control.tar.gz ./data.tar.gz ./debian-binary\n\nairunit_ipk: install\n\t$(eval PKG_NAME := $(shell cat ./ipk/airunit/control/control | grep Package | cut -d\" \" -f2))\n\t$(eval ARCH := $(shell cat ./ipk/airunit/control/control | grep Architecture | cut -d\" \" -f2))\n\t$(eval VERSION :=$(shell cat ./ipk/airunit/control/control | grep Version | cut -d\" \" -f2))\n\t$(eval IPK_NAME := \"${PKG_NAME}_${VERSION}_${ARCH}.ipk\")\n\tmkdir -p ipk/airunit/build\n\techo \"2.0\" > ipk/airunit/build/debian-binary\n\tcp -r ipk/airunit/data ipk/airunit/build/\n\tcp -r ipk/airunit/control ipk/airunit/build/\n\tcd ipk/airunit/build/control && tar czvf ../control.tar.gz .\n\tcd ipk/airunit/build/data && tar czvf ../data.tar.gz .\n\tcd ipk/airunit/build && tar czvf \"../../${IPK_NAME}\" ./control.tar.gz ./data.tar.gz ./debian-binary\n\nipk: install goggle_ipk airunit_ipk\n\nrepo: ipk\n\tmkdir -p repo\n\tcp ipk/*.ipk repo/\n\t../opkg-utils-0.5.0/opkg-make-index ./repo/ > repo/Packages\n\thttp-server -p 8042 ./repo/\n\nclean:\n\trm -rf **/*.o\n\trm -rf *.o\n\trm -rf repo\n\trm -rf libshims/*.so\n\trm -rf msp_displayport_mux\n\trm -rf osd_dji\n\trm -rf ipk/goggle/build\n\trm -rf ipk/airunit/build\n\trm -rf ipk/*.ipk\n\trm -rf obj\n\trm -rf libs\n"
  },
  {
    "path": "Makefile.unix",
    "content": "CC=gcc\nCFLAGS=-I. -O2\nSRCDIR = jni/\nDEPS = $(addprefix $(SRCDIR), msp/msp.h msp/msp_displayport.h net/network.h net/serial.h)\nOSD_OBJ = $(addprefix $(SRCDIR), osd_sfml_udp.o net/network.o msp/msp.o msp/msp_displayport.o)\nDISPLAYPORT_MUX_OBJ = $(addprefix $(SRCDIR), msp_displayport_mux.o net/serial.o net/network.o msp/msp.o)\nOSD_LIBS=-lcsfml-graphics\n\n%.o: %.c $(DEPS)\n\t$(CC) -c -o $@ $< $(CFLAGS)\n\nosd_sfml: $(OSD_OBJ)\n\t$(CC) -o $@ $^ $(CFLAGS) $(OSD_LIBS)\n\nmsp_displayport_mux: $(DISPLAYPORT_MUX_OBJ)\n\t$(CC) -o $@ $^ $(CFLAGS)\n\nclean: \n\trm -rf *.o\n\trm -rf **/*.o\n\trm -f msp_displayport_mux\n\trm -f osd_sfml\n"
  },
  {
    "path": "README.md",
    "content": "# IMPORTANT\nAs of msp-osd v0.12+, the required font format has now changed for the goggles and the OSD Overlay tool.  Support for .bin font file format has been removed in favour of .png font file format.\nSee the 'Fonts' section and the 'Overlaying OSD on DVR' sections further below for clarification.\n\n# MSP-OSD - Full OSD / Displayport OSD\n\nThis package gives you support for full flight controller driven OSD in line with those on analog + other HD systems.\n\nTechnically - it takes MSP DisplayPort (so-called \"canvas\" although this is a misnomer as there is another Betaflight \"canvas\" mode for Pixel OSDs) messages through UDP and renders them to a framebuffer overlaid under the DJI 'dji_glasses' menu system.\n\nSFML (PC/Mac development) and DJI Goggles viewports are available, as well as a *mux* for the Air Unit / Vista, which creates a *pty* and provides filtered MSP access, and reroutes DisplayPort messages to UDP.\n\n# Setup and Installation\n\n## Easy Installation\n\n* Install WTFOS from https://fpv.wtf. WTFOS must be installed on both the goggles and each AU/Vista.\n* Install the msp-osd package on each device using WTFOS.\n* Reboot.\n\n## Flight Controller Setup\n\n* Ensure that the correct UART is set to use MSP\n* Enable MSP DisplayPort\n\n### Betaflight - Since 4.5\n\nBetaflight 4.5 has added support for coloured warning messages/OSD elements.\nThis added feature requires a Betaflight font that includes the extra colour elements.  The extra colour elements are for green, amber and red indicators and respectively reside in additional font pages.\nThis means that to support the extra 3 colours, a 4 page font file is required.\n\nMSP-OSD 0.12+ now supports this feature and includes the required 4 page coloured font for Betaflight bundled by default.\nA Betaflight CLI command may needed to take advantage of the this feature:\n\n`set displayport_msp_fonts = 0,1,2,3`\n\nThis tells Betaflight to use a specific [colour] page when displaying the warning.\n\n##### *Important*\nIf you are **not** using a Betaflight 4 page font, and this value is set to `0,1,2,3`, some OSD elements may not display.  In this case, set the value to `0,0,0,0`.  Meaning every warning will use the first page of the font.\n\n### Betaflight - Since 4.4\n\nBetaflight 4.4 has added native support for OSD in HD aspect ratio.\n\n#### Configure With Preset\n\nThere is a preset available: \"OSD for Fpv.wtf, DJI O3, Avatar HD\", once applied you can then skip ahead to configuring in the OSD tab.\n\n#### Configure Manually\n\nOr to configure manually, first enter the following commands in the CLI tab:\n\n```\nset osd_displayport_device = MSP\nset vcd_video_system = HD\nsave\n```\n\nAnd then in the Ports tab, select the peripheral \"VTX (MSP + DisplayPort)\" for the UART your Vista/Air unit is connected to.\n\n![Ports Tab Setting](/docs/img/ports-vtx.png)\n\nAfterwards, you can configure the OSD elements as normal in the OSD tab.\n\n#### Troubleshooting wrong grid size in BF 4.4 Configurator\n\nIt is recommended to enable [compressed transmission](#compressed-transmission) with BF 4.4; (now default). It removes/avoids ordering issues between FC/AU/Goggles bootup - the AU has to tell the FC the grid size it supports.\n\nIf you don't want to / can't do this - try rebooting your goggles, then reboot your AU.\n\n### Betaflight - 4.3 or Before\n\nWe have a configurator preset available - \"FPV.WTF MSP-OSD\", just be sure to pick the UART your Vista/Air unit is connected to.\n\n#### Or to configure manually\n\nOn *Betaflight*, this is done using the following commands in the CLI tab:\n\n```\nset osd_displayport_device = MSP\nset displayport_msp_serial = <ConfiguratorUART - 1>\nset vcd_video_system = PAL\nsave\n```\n\nEg.: If the Betaflight Configurator says your DJI VTx is attached to UART2, the value for **<ConfiguratorUART - 1>** is **1** - so you would use ```set displayport_msp_serial = 1```.\nTest if the value is correct by typing `save` and after the reboot `get displayport_msp_serial` This command should return the value you set it to. If it returns -1 (and that was not the value you set) then the value was not correct.\n\nFor Betaflight - ensure you set the Video Format to PAL or Auto in the OSD tab - otherwise you don't get access to the whole OSD area. Note that currently BF Configurator hides these options once you switch to MSP for OSD; the last command above should have done this for you.\n\n#### Softserial\n\nWe don't recommend connecting via soft serial; results have been poor - it gives slow/laggy/inconsistent behaviour. But some users have reported it being usable, so if for whatever reason this is your only option, read on.\n\nIf you have connected the Vista/Airunit to a softserial port run the `serial` command to list serial ports\nUse the value after _serial_ with set `displayport_msp_serial` but do **not** subtract 1 from the value. E.g.:\n```\n# serial\nserial 20 1 115200 57600 0 115200\nserial 0 64 115200 57600 0 115200\nserial 1 0 115200 57600 0 115200\nserial 30 1 115200 57600 0 115200\n# set displayport_msp_serial = 30\n```\n\n#### FakeHD\nWith the introduction of HD FPV and its associated transmission and display resolutions, a significantly larger area (canvas*) became a possibility to use for flight controller OSD informaton.\nThe first generation of the DJI FPV system supports a display resolution of 810p (1440px width x 810px height).  This allowed for a potential for a 60 * 22 grid of (mono-spaced) characters of OSD.\nPrior to Betaflight(BF) 4.4, the BF OSD supported a 30 * 16 Grid, which looks large/blocky when displayed in the DJI Goggles.  This 30 * 16 grid was due to the analogue roots of the video and OSD system.\n\nTo present the OSD in a visually better way on HD FPV systems, MSP-OSD introduced a workaround called FakeHD that divided up the OSD canvas area into sections that could be configured.\n\n[FakeHD information is available here.](FAKEHD.md)\n\n### INAV\n\nSelect \"MSP Display Port\" (or \"HDZero VTx\" on older INAV versions) as the Peripheral. Next, select \"HD\" (or the \"WTFOS\" variant) in the OSD tab if you'd like to use the HD Canvas.\n\nIf the iNav OSD appears garbled at first, try entering the iNav menus using the RC sticks, and then exiting the menus. This will force INAV to switch into HD mode a second time.\n\nIt is recommended (now enabled by default in recent msp-osd releases) to enable [compressed transmission](#compressed-transmission) with INAV to avoid issues with the display corrupting - the artifical horizon is the most common element to show this.\n\n### Ardupilot\n\nPlease install a recent Ardupilot version for full functionality. There was one critical bug fix for MSP telemetry not passing through a DisplayPort serial port. Additionally, there were several feature additions including HD support after the last 4.2 release.\n\nSettings:\n\n```\nSERIALx_PROTOCOL = 42\nOSD_TYPE = 5\n```\n\nRecent versions of MSP-OSD fully support Ardupilot with a specific HD FPV font.  This font is bundled with msp-osd 0.12\nIf you wish to use a Betaflight font instead of an Ardupilot font, you can also set `MSP_OPTIONS = 4` to allow the use of a Betaflight font.\n\nMore info: https://ardupilot.org/plane/docs/common-msp-osd-overview-4.2.html#dji-goggles-with-wtf-osd-firmware\n\n### KISS Ultra\n\nSelect MSP on serial and select DJI WTF as canvas dialect. That's it.\n\n### QUICKSILVER\n\nConfigure the UART under Digital VTX - see https://docs.bosshobby.com/Configuring-Quicksilver/#setup\n\n# Fonts\n\nWe bundle in default fonts for Betaflight, Ardupilot, INAV, Quicksilver, and KISS ULTRA (INAV/Betaflight/Ardupilot fonts are SNEAKY_FPV's Unify Europa design - thanks to SNEAKYFPV for allowing us to use these - https://sites.google.com/view/sneaky-fpv/home). Since 0.12 we now use a PNG font format, the same as Walksnail. [Default fonts can be viewed here](fonts). You may also upload your own fonts to the SD card.\n\nFor the naming convention of the font file in png format see the *FC Specific Font File Names* section below.\n\n***It is important to note the following in regards to when a font file is used from where***\n\nWhen the goggles determines what font file to use in presenting the OSD, it will look in the following 3 different locations in preferential order:\n1. SD Card (root) `/storage/sdcard0`\n2. Goggles user font location `/blackbox` (see the *Moving fonts to the goggles file system* section below)\n3. Goggles bundled fallback location `/blackbox/wtfos/opt/fonts`\n\n### HD vs SD fonts\n\nDue to historical support for FC firmware that did not have the full HD canvas grid size, both SD and HD font files are bundled.\nSD font file/s (naming without the suffix *_hd*) are used when the FC configuration is not set to, or does not support, HD. i.e. BF 4.3 and below.\nHD font file/s are used with most modern FC firmware when the equivalent HD selection is made in the FC's OSD configuration tab.\n\n##### *Note*\nYou do not need to use/copy/install the non-hd font file if you only use a HD OSD canvas.\n\n### Attaining/using fonts\n* Download a font package. See below for known community fonts.\n* Place these two PNG files on the root of your Goggles SD card.\n* Insert the SD card and reboot the goggles.\n\n### Using Walksnail Fonts\nAs of msp-osd 0.12, the format of font files are now compatible with Walksnail fonts, with the following caveats.\n\nFor INAV specific Walksnail fonts, the format of the font pages matter as historical fonts typically were formatted with pages stacked vertically.  The new requirement is for the pages to be stacked side by side. The newer format of side by side pages is supported by both msp-osd and Walksnail.  Side by Side specifially formatted Walksnail fonts for INAV will be denoted 'sbs' in the zipfile and or image names.  This does not affect Betaflight as historical fonts were single page, and newer 4 page fonts are side by side formatted pages.  Ardu is a single page font file so all historically created fonts will work without issue.\n\nA Walksnail font package will typically contain 3 files.  An ini file and 2 png files.  The 2 png files will typically follow a naming convention that contains `_24` or `_36` in the file name.  The '24' file is the msp-osd `_hd` equivalent, and the '36' file is the non-hd equivalent.  e.g.\n```\nfont_update.ini\nWS_BFx4_Sphere_24.png\nWS_BFx4_Sphere_36.png\n```\nFrom the above example, to use them for msp-osd, copy the 2 png files to the SD card root and rename them as:\n|Walksnail Name|msp-osd Name|\n|--------------|------------|\n|`WS_BFx4_Sphere_24.png`|`font_btfl_hd.png`|\n|`WS_BFx4_Sphere_36.png`|`font_btfl.png`|\n\n### .bin Fonts\n\nDue to the changes in 0.12, the .bin font format was superseded in favour of a wider accepted format that makes it easier for the community to create their own.\n\nWith 0.12, any fonts stored on the SD card in the .bin format will be ignored.\n\nIf you wish to retain the font from the .bin file, this open source tool will assist in conversion to png from bin.\nhttps://github.com/shellixyz/hd_fpv_osd_font_tool/tree/main/src/bin/hd_fpv_osd_font_tool\n\n### FC Specific Font File Names\n\n| Flight controller | SD | HD |\n| ----------------- | -- | -- |\n| Betaflight       | `font_btfl.png` | `font_btfl_hd.png` |\n| INAV       | `font_inav.png` | `font_inav_hd.png`|\n| Ardupilot       | `font_ardu.png` | `font_ardu_hd.png`|\n| KISS Ultra       | `font_ultr.png` | `font_ultr_hd.png`|\n| QUICKSILVER       | `font_quic.png` | `font_quic_hd.png`|\n| Generic/Fallback*       | `font.png` | `font_hd.png`|\n\n*This uses the Betaflight font layout\n\nAirside VTx (AU/Vista) which have a very old version of msp-osd on, as well as flight controllers which do not respond to the Variant request, like old Ardupilot versions, will use to the Generic/Fallback font.\n##### *Note*\nYou can also add fonts for firmwares not in this list; using the generic filename, or put the MSP identifier in (lower case it) the filename - ```font_<fc_variant>.png / font_<fc_variant>_hd.png```\n\n### Moving fonts to the goggles file system\n\nIf you wish to use a specific font different than the font stored on the goggles, for when you do not have the SD card inserted in the goggles, you can follow the below steps to do this.\n1. Copy the font files you wish to be stored on the goggles firstly to your SD card\n2. Connect your goggles via USB if you didn't do this for step 1.\n3. Insert the SD card in the goggles then power them on\n4. Navigate to https://fpv.wtf\n5. After the goggles are found and connected, go to the fpv.wtf CLI and type the following\n6. `cp /storage/sdcard0/font*.png /blackbox`\n7. Power off the goggles, remove the SD card and confirm the newly copied font is used.\n\n##### *Note*\nTo remove copied fonts repeat steps 1 to 6 and replace step 7 with `rm /blackbox/font*.png`.  Then power off and on the goggles.\n\n### Suggested Third Party Fonts\n\n - [KNIFA's Material](https://github.com/Knifa/material-osd/releases) - use the Walksnail version for MSP-OSD <= 0.12\n - [SNEAKY_FPV's colour fonts for INAV, ARDU and BF](https://sites.google.com/view/sneaky-fpv/home)\n - [VICEWIZE Italic](https://github.com/vicewize/vicewizeosdfontset)\n - [Kw0ngk4n's Neue OSD](https://github.com/Kw0ngk4n/WTF-Neue-OSD)\n - [EVilm1's OSD Font](https://github.com/EVilm1/EVilm1-OSD-Font)\n\n\n## Overlaying OSD on DVR\n**Note the change in font file format requirements (png format) for the OSD Overlay tool.  If no font file is provided by the user, a built in font (png format) will be used.**\n\nThe overlay process uses DVR, recorded osd data and a font file, to overlay/render the OSD data onto DVR footage on your computer.\nA default font will be used if no font file is supplied however.  The font look and feel will be the bundled font you would see if using this during flight.\n\nhttps://fpv.wtf/osd-overlay provides a tool that will overlay captured osd information onto DVR footage.\n### Pre-requisites\nUnless the osd information is captured during DVR recording on the goggles you will be unable to overlay your osd onto DVR on your computer.\n\nThe following fpv.wtf CLI commands (goggles) will ensure this is enabled.\n\n```\npackage-config set msp-osd rec_enabled true\npackage-config apply msp-osd\n```\n\n##### *Notes*\nYou only need to supply a font file for the canvas the DVR was recorded with.  i.e. If your FC firmware configuration was HD or a HD variant you only need to supply the '_hd.png' font file.\nThe 'Chroma Key' will replace the DVR with a solid colour for use within video editing software.  Be aware however that fonts have an amount of transparency around elements that will include the 'Chroma Key' bleed that will be difficult to avoid in video editing software.\n\nSee the 'Using Walksnail Fonts Section' for specific requirements that may apply to font file formats.\n\n# Configuration options\n\nConfiguration options can be set using the WTFOS Configurator CLI.\n\nPrefix option with `package-config set msp-osd`\n\ne.g. `package-config set msp-osd compress_osd true`\n\nOnce desired setting changes are made then: `package-config apply msp-osd`, otherwise settings will be lost when power is off.\n\nVisit https://fpv.wtf/package/fpv-wtf/msp-osd with your Goggles or Air Unit plugged in to edit options.\n\n### Current available options (Goggles):\n\n| Option | Description | Type | Default|\n| ------ | ----------- | ---- |--------|\n|`show_waiting`| enables or disables WAITING FOR OSD message | true/false | true |\n|`show_au_data`| enables AU data (temp/voltage) overlay on the right | true/false | false |\n|`rec_enabled`| enable OSD recording to .msp files alongside video | true/false | true |\n|`rec_pb_enabled`| enable OSD playback if .msp file is stored alongside video | true/false | true |\n|`hide_diagnostics`| hide the diagnostic information in the bottom right | true/false | false |\n|`fakehd_enable`| enables [FakeHD](#FakeHD); the other FakeHD options don't do anything if this is disabled. FakeHD is force disabled if the Flight Controller supports proper HD / RealHD | true/false| false |\n|`fakehd_lock_center`| Lock FakeHD in centered mode all the time; no gaps/spreading out even when you are flying. | true/false | false |\n|`fakehd_menu_switch`| FakeHD will use this character as the menu switch to detect when you are in menus/postflight and triggger centering. | integer/number | 4 (Betaflight Throttle) |\n|`fakehd_hide_menu_switch`| FakeHD will hide the menu switch set above; and the next 5 characters | true / false | false |\n| `fakehd_columns` | FakeHD column alignment config | Single character, one of T M B S | S |\n| `fakehd_rows` | FakeHD row alignment config, each character configures the alignment for one row | 16 characters, each one of L C R W T F D | WWWWWWCCWWWWWWWD |\n\n\n\n### Current available options (Air Unit/Vista):\n\n| Option | Description | Type | Default|\n| ------ | ----------- | ---- |--------|\n|`compress_osd`| Enable sending full frames of compressed data. Disable to send raw MSP data [Read more](#Compressed-Transmission) | true/false| true |\n| `osd_update_rate_hz` | Configure the update rate in hz for the OSD when using compressed transmission | integer | 10 |\n| `disable_betaflight_hd` | Disable HD Mode, which is otherwise set by default in Betaflight 4.4 | true/false | false |\n| `fast_serial` | Change serial baud rate to 230400 baud, which can improve OSD performance in some situations - FC UART config must be changed to match. | true/false | false |\n| `cache_serial` | Cache unimportant MSP messages for seldom-used features (like PID tuning in the DJI Goggles Settings Menu) to reduce serial pressure | true/false | false |\n\n#### Compressed Transmission\n\nAs of 0.7.0, a new option, `compress_osd`, was added to the **air side** process.\n\nIf this is set to \"true\", then the entire character buffer will be sent using LZ4 compression at the rate defined in `osd_update_rate_hz`, instead of sending raw MSP messages over the air.\n\nWhen enabled, this should fix INAV delta update related issues as well as provide better link stability.\n\nTo enable:\n\nVisit https://fpv.wtf/package/fpv-wtf/msp-osd with your **Air Unit / Vista** plugged in to edit package options.\n\nThis option is enabled by default as of 0.10.0, however, if you upgraded from an older version, your configuration will need to be updated using the configurator.\n\nIf you continue to have issues with especially INAV character corruption, it is likely your serial link is saturated. Check that the \"Custom OSD\" option in your DJI goggles menus is set to _disabled_ , and also try out the cache_serial option.\n\n## FAQ / Suggestions\n\n### Modify / Move original DJI OSD elements\n\nYou can now modify the elements present in the original DJI OSD. These include for example : transmission speed, latency, channel used, googles battery, sd card icon and default timer.\n\nElements position, visibility, font and icons can be modified by editing the internal googles files.\nThis is possible by connecting to the googles using ADB. You can even preview changes using a Python script!\n\nThis is not a trivial thing for everyone to do, the full tutorial can be found [here](https://github.com/EVilm1/WIKI-HACK-DJI-OSD#6-advanced-setup-modify-the-dji-hud-elements).\n\n### How do I create a new font (for INAV, Ardupilot, etc.)?\n\nUse `mcm2img` , specifically Knifa's branch to allow you to draw using a PNG template.\n\nhttps://github.com/Knifa/mcm2img/tree/templates\n\n### Why is everything so big / can I make the text smaller (betaflight)?\n\nFor Betaflight prior to 4.4, look into FakeHD.\nFor Betaflight after 4.4, you should see \"HD\" fonts by default. Make sure your VTx (AU/Vista) is powered up and visit the Betaflight Configurator to move OSD items to the edge of the screen.\n\n### How can I get my INAV/ArduPilot/Kiss Ultra OSD closer to the edge of the screen / Why is FakeHD closer to the edges?\n\n - The goggles need 60 characters to go edge to edge - so the 50 in the hd grid doesn't quite fill it\n - So, depending on the Flight Controller's setup, the RealHD grid is displayed centered in the goggles - gaps on both edges.\n - FakeHD had no compatibility constraints like this so we were able to use the full width of the screens.\n - Consequently, FakeHD can get nearer the edges.\n - Currently no solution to get RealHD closer to the edges.\n\n### What is RealHD\n\nSometimes we refer to the proper MSP OSD HD grid supported by ArduPilot / Kiss Ultra / INAV / Betaflight (from 4.4) + others as RealHD, to distinguish from FakeHD.\n\n# Compiling (development and debugging)\n\nTo build for DJI, install the [Android NDK](https://developer.android.com/ndk/downloads) and add the NDK toolchain to your PATH, then use `ndk-build` to build the targets.\n\nTo build for UNIXes, install CSFML and run:\n\n```\nmake -f Makefile.unix\n```\n\nProvided targets and tools are:\n\n* `msp_displayport_mux` - takes MSP DisplayPort messages, bundles each frame (all DisplayPort messages between Draw commands) into a single UDP Datagram, and then blasts it over UDP. Also creates a PTY that passes through all _other_ MSP messages, for `dji_hdvt_uav` to connect to.\n* `libdisplayport_osd_shim.so` - Patches the `dji_glasses` process to listen for these MSP DisplayPort messages over UDP, and blits them to a DJI framebuffer screen using the DJI framebuffer HAL `libduml_hal` access library, and a converted Betaflight font stored in `font.bin`.\n* `osd_sfml` - The same thing as `osd_dji`, but for a desktop PC using SFML and `bold.png`.\n\nAdditional debugging can be enabled using `-DDEBUG` as a CFLAG.\n\n## Custom Build Installation (Goggles)\n\nThere's a slightly different process for V1 vs V2 Goggles, they renamed some bits between the two.\n\n### FPV Goggles V1\n\n```\nndk-build\nadb shell setprop dji.dji_glasses_service 0\nadb push libs/armeabi-v7a/libdisplayport_osd_shim.so /tmp\nadb shell LD_PRELOAD=/tmp/libdisplayport_osd_shim.so dji_glasses_original -g\n```\n\n### FPV Goggles V2\n\n```\nndk-build\nadb shell setprop dji.glasses_wm150_service 0\nadb push libs/armeabi-v7a/libdisplayport_osd_shim.so /tmp\nadb shell LD_PRELOAD=/tmp/libdisplayport_osd_shim.so dji_gls_wm150_original -g\n```\n\n### Air Unit / Air Unit Lite (Vista)\n\n```\nndk-build\nadb push msp_displayport_mux /blackbox\nadb shell setprop dji.hdvt_uav_service 0\nadb shell mv /dev/ttyS1 /dev/ttyS1_moved\nadb shell nohup /blackbox/msp_displayport_mux 192.168.41.2 /dev/ttyS1_moved /dev/ttyS1 &\nadb shell setprop dji.hdvt_uav_service 1\n```\n\nThis tells the displayport mux to send data from /dev/ttyS1_moved to 192.168.41.2 (goggles) and to create a fake serial port at /dev/ttyS1 with the displayport messages filtered out.\n\nOptionally, you can add `-f`, like `nohup /blackbox/msp_displayport_mux -f 192.168.41.2 /dev/ttyS1_moved /dev/ttyS1` to put the serial port in a faster 230400 baud mode, and set the MSP serial port in your flight controller to 230400 to try to improve the framerate.\n\nYou can also omit `setprop dji.hdvt_uav_service 1` , which will improve your OSD framerate at the expense of disabling all Air Unit / Vista side coordination functionality (AU recording, channel changes, some RC features, etc.).\n\nEnjoy.\n\n## Additional Reading / Learning\n\nhttps://github.com/fpv-wtf/margerine/wiki\n\n## Shoutouts / Thank You\n\n* http://github.com/fpv-wtf team, for making this all possible and very helpful random bits of advice\n"
  },
  {
    "path": "config/airunit/config.json",
    "content": "{\n    \"fast_serial\": false,\n    \"cache_serial\": false,\n    \"osd_update_rate_hz\": 10,\n    \"compress_osd\": true,\n    \"disable_betaflight_hd\": false\n}"
  },
  {
    "path": "config/airunit/schema.json",
    "content": "{\n    \"settings\": {\n      \"fast_serial\": {\n        \"name\": \"Fast Serial\",\n        \"widget\": \"checkbox\"\n      },\n      \"cache_serial\": {\n        \"name\": \"Cache Responses\",\n        \"widget\": \"checkbox\"\n      },\n      \"compress_osd\": {\n        \"name\": \"Compress OSD\",\n        \"widget\": \"checkbox\"\n      },\n      \"osd_update_rate_hz\": {\n        \"name\": \"OSD update rate\",\n        \"widget\": \"range\",\n        \"min\": 2,\n        \"max\": 20,\n        \"step\":1\n      },\n      \"disable_betaflight_hd\": {\n        \"name\": \"Compress OSD\",\n        \"widget\": \"checkbox\"\n      }\n    },\n    \"units\": [\n      \"msp-osd-airside\"\n    ]\n  }"
  },
  {
    "path": "config/airunit/schemaV2.json",
    "content": "{\n    \"title\": \"MSP OSD\",\n    \"description\": \"Airside OSD config\",\n    \"type\": \"object\",\n    \"units\": [\n        \"msp-osd-airside\"\n    ],\n    \"required\": [],\n    \"properties\": {\n        \"compress_osd\": {\n            \"title\": \"Compress OSD\",\n            \"type\": \"boolean\",\n            \"description\": \"\",\n            \"enum\": [\n                true,\n                false\n            ]\n        },\n        \"osd_update_rate_hz\": {\n            \"title\": \"OSD update rate (for Compressed OSD)\",\n            \"type\": \"number\",\n            \"description\": \"\",\n            \"minimum\": 1,\n            \"maximum\": 20\n        },\n        \"disable_betaflight_hd\": {\n            \"title\": \"Disable Betaflight HD Mode\",\n            \"type\": \"boolean\",\n            \"description\": \"\",\n            \"enum\": [\n                true,\n                false\n            ]\n        },\n        \"fast_serial\": {\n            \"type\": \"boolean\",\n            \"title\": \"Fast Serial\",\n            \"description\": \"\",\n            \"enum\": [\n                true,\n                false\n            ]\n        },\n        \"cache_serial\": {\n            \"type\": \"boolean\",\n            \"title\": \"Cache Responses\",\n            \"description\": \"\",\n            \"enum\": [\n                true,\n                false\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "config/airunit/uiSchemaV2.json",
    "content": "{\n    \"compress_osd\": {\n        \"ui:help\": \"Enable sending full frames of compressed data. Disable to send raw MSP data  - we strongly recommend leaving this on. [Read more](https://github.com/fpv-wtf/msp-osd#Compressed-Transmission)\"\n    },\n    \"osd_update_rate_hz\": {\n        \"ui:help\": \"Configure the update rate in hz for the OSD when using compressed transmission (default 10)\"\n    },\n    \"disable_betaflight_hd\": {\n        \"ui:help\": \"Disable HD Mode, which is otherwise set by default if configured in Betaflight 4.4\"\n    },\n    \"fast_serial\": {\n        \"ui:help\": \"Change serial baud rate to 230400 baud, which can improve OSD performance in some situations - FC UART config must be changed to match.\"\n    },\n    \"cache_serial\": {\n        \"ui:help\": \"Cache unimportant MSP messages for seldom-used features (like PID tuning in the DJI Goggles Settings Menu) to reduce serial pressure\"\n    }\n}"
  },
  {
    "path": "config/goggles/config.json",
    "content": "{\n    \"show_waiting\": true,\n    \"show_au_data\": false,\n    \"fakehd_enable\": false,\n    \"fakehd_hide_throttle_element\": false,\n    \"fakehd_lock_center\": false,\n    \"fakehd_menu_switch\": 4,\n    \"fakehd_hide_menu_switch\": false,\n    \"fakehd_layout_debug\": false,\n    \"fakehd_columns\": \"S\",\n    \"fakehd_rows\": \"WWWWWWCCWWWWWWWD\",\n    \"rec_enabled\": true,\n    \"rec_pb_enabled\": true\n}\n"
  },
  {
    "path": "config/goggles/schema.json",
    "content": "{\n\t\"settings\": {\n\t\t\"show_waiting\": {\n\t\t\t\"name\": \"Show Waiting Message\",\n\t\t\t\"widget\": \"checkbox\"\n\t\t},\n\t\t\"show_au_data\": {\n\t\t\t\"name\": \"Show VTx Temperature and Voltage\",\n\t\t\t\"widget\": \"checkbox\"\n\t\t},\n\t\t\"fakehd_enable\": {\n\t\t\t\"name\": \"Enable FakeHD Mode\",\n\t\t\t\"widget\": \"checkbox\"\n\t\t},\n\t\t\"fakehd_hide_throttle_element\": {\n\t\t\t\"name\": \"FakeHD hide throttle element\",\n\t\t\t\"widget\": \"checkbox\"\n\t\t},\n\t\t\"fakehd_lock_center\": {\n\t\t\t\"name\": \"Lock FakeHD to centered mode\",\n\t\t\t\"widget\": \"checkbox\"\n\t\t},\n\t\t\"fakehd_menu_switch\": {\n\t\t\t\"name\": \"FakeHD menu switch character\",\n\t\t\t\"widget\": \"range\",\n\t\t\t\"min\": 1,\n\t\t\t\"max\": 512,\n\t\t\t\"step\":1\n\t\t},\n\t\t\"fakehd_hide_menu_switch\": {\n\t\t\t\"name\": \"FakeHD hide the menu switch\",\n\t\t\t\"widget\": \"checkbox\"\n\t\t},\n\t\t\"fakehd_layout_debug\": {\n\t\t\t\"name\": \"Undocumented feature to fill the grid with numbers for layout debugging\",\n\t\t\t\"widget\": \"checkbox\"\n\t\t},\n\t\t\"fakehd_columns\": {\n\t\t\t\"name\": \"FakeHD column config\",\n\t\t\t\"widget\": \"text\",\n\t\t\t\"pattern\": \"[TMBS]\",\n\t\t\t\"minLength\": 1,\n\t\t\t\"maxLength\": 1\n\t\t},\n\t\t\"fakehd_rows\": {\n\t\t\t\"name\": \"FakeHD row config\",\n\t\t\t\"widget\": \"text\",\n\t\t\t\"pattern\": \"[LCRWTFD]{16}\",\n\t\t\t\"minLength\": 16,\n\t\t\t\"maxLength\": 16\n\t\t},\n\t\t\"rec_enabled\": {\n\t\t\t\"name\": \"Enable OSD recording\",\n\t\t\t\"widget\": \"checkbox\"\n\t\t},\n\t\t\"rec_pb_enabled\": {\n\t\t\t\"name\": \"Enable OSD playback\",\n\t\t\t\"widget\": \"checkbox\"\n\t\t},\n\t\t\"hide_diagnostics\": {\n\t\t\t\"name\": \"Hide diagnostic information\",\n\t\t\t\"widget\": \"checkbox\"\n\t\t}\n\t},\n\t\"units\": [\n\t\t\"msp-osd-goggles\"\n\t]\n}\n"
  },
  {
    "path": "config/goggles/schemaV2.json",
    "content": "{\n    \"title\": \"MSP OSD - Goggles\",\n    \"description\": \"\",\n    \"type\": \"object\",\n    \"units\": [ \"msp-osd-goggles\" ],\n    \"required\": [],\n    \"properties\": {\n        \"show_waiting\": {\n            \"type\": \"boolean\",\n            \"title\": \"Show Waiting Message\",\n            \"description\": \"\",\n            \"enum\": [\n                true,\n                false\n            ]\n        },\n        \"show_au_data\": {\n            \"type\": \"boolean\",\n            \"title\": \"Show VTx Temperature and Voltage\",\n            \"description\": \"\",\n            \"enum\": [\n                true,\n                false\n            ]\n        },\n        \"rec_enabled\": {\n            \"type\": \"boolean\",\n            \"title\": \"Enable OSD recording\",\n            \"description\": \"\",\n            \"enum\": [\n                true,\n                false\n            ]\n        },\n        \"rec_pb_enabled\": {\n            \"type\": \"boolean\",\n            \"title\": \"Enable OSD playback\",\n            \"description\": \"\",\n            \"enum\": [\n                true,\n                false\n            ]\n        },\n        \"hide_diagnostics\": {\n            \"type\": \"boolean\",\n            \"title\": \"Hide diagnostic information\",\n            \"description\": \"\",\n            \"enum\": [\n                true,\n                false\n            ]\n        },\n        \"fakehd_enable\": {\n            \"type\": \"boolean\",\n            \"title\": \"Enable FakeHD Mode\",\n            \"description\": \"\",\n            \"enum\": [\n                true,\n                false\n            ]\n        }\n    },\n    \"dependencies\": {\n        \"fakehd_enable\": {\n            \"oneOf\": [\n                {\n                    \"properties\": {\n                        \"fakehd_enable\": {\n                            \"enum\": [\n                                false\n                            ]\n                        }\n                    }\n                },\n                {\n                    \"properties\": {\n                        \"fakehd_enable\": {\n                            \"enum\": [\n                                true\n                            ]\n                        },\n                        \"fakehd_lock_center\": {\n                            \"type\": \"boolean\",\n                            \"title\": \"Lock FakeHD to centered mode\",\n                            \"description\": \"\",\n                            \"enum\": [\n                                true,\n                                false\n                            ]\n                        },\n                        \"fakehd_menu_switch\": {\n                            \"type\": \"number\",\n                            \"title\": \"FakeHD menu switch character\",\n                            \"description\": \"\",\n                            \"minimum\": 1,\n                            \"maximum\": 512\n                        },\n                        \"fakehd_hide_menu_switch\": {\n                            \"type\": \"boolean\",\n                            \"title\": \"FakeHD hide the menu switch\",\n                            \"description\": \"\",\n                            \"enum\": [\n                                true,\n                                false\n                            ]\n                        },\n                        \"fakehd_columns\": {\n                            \"type\": \"string\",\n                            \"title\": \"FakeHD column config\",\n                            \"description\": \"\",\n                            \"oneOf\": [\n                                {\n                                    \"const\": \"T\",\n                                    \"title\": \"Top\"\n                                },\n                                {\n                                    \"const\": \"M\",\n                                    \"title\": \"Middle\"\n                                },\n                                {\n                                    \"const\": \"B\",\n                                    \"title\": \"Bottom\"\n                                },\n                                {\n                                    \"const\": \"S\",\n                                    \"title\": \"Split\"\n                                }\n                            ]\n                        },\n                        \"fakehd_rows\": {\n                            \"type\": \"string\",\n                            \"title\": \"FakeHD row config\",\n                            \"description\": \"16 characters; each one of LCRWTFD\",\n                            \"maxLength\": 16,\n                            \"minLength\": 16,\n                            \"pattern\": \"^[LCRWTFD]{16}$\"\n                        }\n                    }\n                }\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "config/goggles/uiSchemaV2.json",
    "content": "{\n    \"show_waiting\": {\n        \"ui:help\": \"Enables or disables WAITING FOR OSD message\"\n    },\n    \"show_au_data\": {\n        \"ui:help\": \"Enables AU data (temp/voltage) overlay on the right\"\n    },\n    \"rec_enabled\": {\n        \"ui:help\": \"Enable OSD recording to .msp files alongside video\"\n    },\n    \"rec_pb_enabled\": {\n        \"ui:help\": \"Enable OSD playback if .msp file is stored alongside video (ie: OSD recording is enabled)\"\n    },\n    \"hide_diagnostics\": {\n        \"ui:help\": \"Hide the diagnostic information in the bottom right\"\n    },\n    \"fakehd_enable\": {\n        \"ui:help\": \"Enables FakeHD. [Read more](https://github.com/fpv-wtf/msp-osd#fakehd)\"\n    },\n    \"fakehd_lock_center\": {\n        \"ui:help\": \"Lock FakeHD in centered mode all the time; no gaps/spreading out even when you are flying. [Read more](https://github.com/fpv-wtf/msp-osd#i-dont-want-gaps-at-all)\"\n    },\n    \"fakehd_menu_switch\": {\n        \"ui:help\": \"FakeHD will use this character as the menu switch to detect when you are in menus/postflight and triggger centering. [Read more](https://github.com/fpv-wtf/msp-osd#menu-switching---getting-rid-of-gaps-when-displaying-menu--post-flight-stats--displaying-centered)\"\n    },\n    \"fakehd_hide_menu_switch\": {\n        \"ui:help\": \"FakeHD will hide the menu switch set above; and the next 5 characters\"\n    },\n    \"fakehd_columns\": {\n        \"ui:help\": \"FakeHD column alignment config. [Read more](https://github.com/fpv-wtf/msp-osd#customising-the-default-fakehd-grid)\"\n    },\n    \"fakehd_rows\": {\n        \"ui:help\": \"FakeHD row alignment config, each character configures the alignment for one row. [Read more](https://github.com/fpv-wtf/msp-osd#customising-the-default-fakehd-grid)\"\n    }\n}"
  },
  {
    "path": "docs/fonts/gen.sh",
    "content": "#!/bin/bash\n\n\necho \"You need https://github.com/shellixyz/hd_fpv_osd_font_tool\";\necho \"And you need montage from imagemagick\";\n\n\n\necho \"converting\";\nfind ../../fonts -iname '*.bin' | cut -d '/' -f 4 | cut -d '.' -f 1 | xargs -I '%' ~/.cargo/bin/hd_fpv_osd_font_tool convert djibin:../../fonts/%.bin tiledir:dir_%;\n\necho \"montaging\";\nfind ../../fonts -iname '*.bin' | cut -d '/' -f 4 | cut -d '.' -f 1 | xargs -I '{}' montage 'dir_{}/*.png'  -tile 16x -geometry 24x36+1+1 {}.png;\n\necho \"cleaning\";\nrm -rf ./dir_font*\n\n\necho \"Finished\""
  },
  {
    "path": "ipk/airunit/control/conffiles",
    "content": "/opt/etc/package-config/msp-osd/config.json\n"
  },
  {
    "path": "ipk/airunit/control/control",
    "content": "Package: msp-osd\nVersion: 0.10.1\nMaintainer: bri3d\nDescription: MSP OSD service for the DJI HD FPV airunit.\nArchitecture: pigeon-airside\nDepends: dinit, wtfos-opkg-config, wtfos-package-config\nHomepage: https://github.com/fpv-wtf/msp-osd\n"
  },
  {
    "path": "ipk/airunit/control/postinst",
    "content": "#!/system/bin/sh\n/opt/sbin/dinitctl -u enable msp-osd-airside || true\n"
  },
  {
    "path": "ipk/airunit/control/prerm",
    "content": "#!/system/bin/sh\n/opt/sbin/dinitctl -u disable msp-osd-airside || true\n\n"
  },
  {
    "path": "ipk/airunit/data/opt/bin/airunit-osd-start.sh",
    "content": "#!/system/bin/sh\nsetprop dji.hdvt_uav_service 0\nsetprop dji.shuttle_service 0\n\nif [ ! -e \"/dev/ttyS1_moved\" ]\nthen\n  mv /dev/ttyS1 /dev/ttyS1_moved\nfi\n\n/opt/bin/msp_displayport_mux 192.168.41.2 /dev/ttyS1_moved /dev/ttyS1 &\necho $! > /opt/var/run/airunit-osd-dji.pid\nsetprop dji.hdvt_uav_service 1\nsetprop dji.shuttle_service 1\n\n"
  },
  {
    "path": "ipk/airunit/data/opt/etc/dinit.d/msp-osd-airside",
    "content": "type = bgprocess\ncommand = /opt/bin/airunit-osd-start.sh\npid-file = /opt/var/run/airunit-osd-dji.pid\nrestart = true\n"
  },
  {
    "path": "ipk/goggle/control/conffiles",
    "content": "/opt/etc/package-config/msp-osd/config.json\n"
  },
  {
    "path": "ipk/goggle/control/control",
    "content": "Package: msp-osd\nVersion: 0.12.4\nMaintainer: bri3d\nDescription: MSP OSD service for the DJI HD FPV goggles.\nArchitecture: pigeon-glasses\nDepends: wtfos-modloader, wtfos-package-config\nHomepage: https://github.com/fpv-wtf/msp-osd\n"
  },
  {
    "path": "ipk/goggle/control/postinst",
    "content": "#!/system/bin/sh\n/opt/sbin/dinitctl -u enable msp-osd-goggles || true\n"
  },
  {
    "path": "ipk/goggle/control/preinst",
    "content": "#!/system/bin/sh\nif [[ -f /opt/fonts ]]; then\n    rm -f /opt/fonts\nfi\n/opt/sbin/dinitctl -u disable msp-osd-goggles || true\n"
  },
  {
    "path": "ipk/goggle/control/prerm",
    "content": "#!/system/bin/sh\n/opt/sbin/dinitctl -u disable msp-osd-goggles || true\n"
  },
  {
    "path": "ipk/goggle/data/opt/etc/dinit.d/msp-osd-goggles",
    "content": "type = scripted\ncommand = modmanager enable diy_glasses displayport_osd_shim\nstop-command = modmanager disable diy_glasses displayport_osd_shim"
  },
  {
    "path": "jni/Android.mk",
    "content": "LOCAL_PATH := $(call my-dir)\ninclude $(CLEAR_VARS)\n\nLOCAL_MODULE := duml_hal\nLOCAL_SRC_FILES := libduml_hal.so\nLOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include\ninclude $(PREBUILT_SHARED_LIBRARY)\ninclude $(CLEAR_VARS)\n\nLOCAL_CFLAGS += -fPIC -std=c99 -O3\nLOCAL_LDFLAGS += -fPIC\nLOCAL_LDLIBS := -llog -lz\nLOCAL_ARM_NEON := true\nLOCAL_MODULE := displayport_osd_shim\nLOCAL_SHARED_LIBRARIES := duml_hal\nLOCAL_SRC_FILES := \\\n\tdisplayport_osd_shim.c \\\n\tfakehd/fakehd.c \\\n\tfont/font.c \\\n\thw/dji_display.c \\\n\thw/dji_radio_shm.c \\\n\thw/dji_services.c \\\n\tjson/osd_config.c \\\n\tjson/parson.c \\\n\tlz4/lz4.c \\\n\tmsp/msp_displayport.c \\\n\tmsp/msp.c \\\n\tnet/network.c \\\n\tosd_dji_overlay_udp.c \\\n\trec/rec_pb.c \\\n\trec/rec_shim.c \\\n\trec/rec_util.c \\\n\trec/rec.c \\\n\ttoast/toast.c \\\n\tutil/fs_util.c \\\n\tlibspng/spng.c\ninclude $(BUILD_SHARED_LIBRARY)\n\ninclude $(CLEAR_VARS)\n\nLOCAL_SRC_FILES:= \\\n\thw/dji_radio_shm.c \\\n\tjson/osd_config.c \\\n\tjson/parson.c \\\n\tmsp_displayport_mux.c \\\n\tmsp/msp.c \\\n\tmsp/msp_displayport.c \\\n\tnet/network.c \\\n\tnet/serial.c \\\n\tutil/fs_util.c \\\n\tlz4/lz4.c\nLOCAL_MODULE := msp_displayport_mux\ninclude $(BUILD_EXECUTABLE)\ninclude $(CLEAR_VARS)\n"
  },
  {
    "path": "jni/Application.mk",
    "content": "APP_PLATFORM=android-23\nAPP_ABI=armeabi-v7a\nAPP_CFLAGS += -DSTDC_HEADERS\nAPP_OPTIM := release\n"
  },
  {
    "path": "jni/displayport_osd_shim.c",
    "content": "#include <stdlib.h>\n#include <stdbool.h>\n#include <unistd.h>\n#include <dlfcn.h>\n#include <string.h>\n\n#include \"osd.h\"\n#include \"hw/dji_display.h\"\n#include \"hw/dji_services.h\"\n\n// Which window in the creation order is the overlay / top menu. Usually 1.\n#define MENU_WINDOW_ORDER 1\n\nstatic duss_disp_instance_handle_t *disp_instance = NULL;\nstatic duss_hal_obj_handle_t ion_handle = NULL;\nstatic int started = 0;\nstatic int are_v2 = 0;\nstatic int window_count = 0;\nstatic void *menu_window = NULL;\n\n// Patch a seemingly unused window management thread in libtp1801_gui.so to inject our code into the dji_glasses process.\nvoid _ZN24MMSFBWindowManagerThread10threadMainEv(void *this) {\n    printf(\"ENTERING MAIN \\n\");\n    are_v2 = dji_goggles_are_v2();\n    while(1) {\n        if(started == 0 && disp_instance != NULL && ion_handle != NULL) {\n            printf(\"ENTERING OSD! fbdev disp %x ion %x\\n\", disp_instance, ion_handle);\n            started = 1;\n            osd_directfb(disp_instance, ion_handle);\n        }\n        usleep(50000);\n    }\n}\n\nstatic void (*_ZN23GlassVideoChnlUIManager19setNextVideoChannelE19GlassVideoChannelId2)(void *this, uint32_t channel_id) = NULL;\nstatic void *tp1801_gui_lib = NULL;\n\nvoid _ZN23GlassVideoChnlUIManager19setNextVideoChannelE19GlassVideoChannelId(void *this, uint32_t channel_id) {\n    if (_ZN23GlassVideoChnlUIManager19setNextVideoChannelE19GlassVideoChannelId2 == NULL) {\n        _ZN23GlassVideoChnlUIManager19setNextVideoChannelE19GlassVideoChannelId2 = dlsym(RTLD_NEXT, \"_ZN23GlassVideoChnlUIManager19setNextVideoChannelE19GlassVideoChannelId\");\n        if (_ZN23GlassVideoChnlUIManager19setNextVideoChannelE19GlassVideoChannelId2 == NULL) {\n            tp1801_gui_lib = dlopen(\"/system/lib/libtp1801_gui.so\", 1);\n            _ZN23GlassVideoChnlUIManager19setNextVideoChannelE19GlassVideoChannelId2 = dlsym(tp1801_gui_lib, \"_ZN23GlassVideoChnlUIManager19setNextVideoChannelE19GlassVideoChannelId\");\n            if (_ZN23GlassVideoChnlUIManager19setNextVideoChannelE19GlassVideoChannelId2 == NULL) {\n                printf(\"dlsym: %s\\n\", dlerror());\n            }\n        }\n    }\n    // RTOS video channels:\n    // 1 = Playback\n    // 2 = Unknown\n    // 3 = Live Video\n    // 4 = AV-IN\n    if(channel_id == 3) {\n        osd_enable();\n    } else {\n        osd_disable();\n    }\n    _ZN23GlassVideoChnlUIManager19setNextVideoChannelE19GlassVideoChannelId2(this, channel_id);\n}\n\nstatic void *hal_lib = NULL;\n\nstatic duss_result_t (*duss_hal_mem_alloc2)(duss_hal_obj_handle_t handle, duss_hal_mem_handle_t *mem_handle, uint32_t size, uint32_t param1, uint32_t param2, uint32_t param3) = 0;\n// Patch libduml_hal's duss_hal_mem_alloc.\n// Use the first invocation of this to steal the duss_hal_obj_handle_t pointing to the ion shared memory service.\nduss_result_t duss_hal_mem_alloc(duss_hal_obj_handle_t handle, duss_hal_mem_handle_t *mem_handle, uint32_t size, uint32_t param1, uint32_t param2, uint32_t param3) {\n    if (duss_hal_mem_alloc2 == NULL) {\n\t    duss_hal_mem_alloc2 = dlsym(RTLD_NEXT, \"duss_hal_mem_alloc\");\n\t\tif (duss_hal_mem_alloc2 == 0){\n\t\t\tif (hal_lib == NULL){\n\t\t\t\thal_lib = dlopen(\"/system/lib/libduml_hal.so\", 1);\n\t\t\t}\n\t\t\tduss_hal_mem_alloc2 = dlsym(hal_lib, \"duss_hal_mem_alloc\");\n\t\t\tif (duss_hal_mem_alloc2 == 0) {\n\t\t\t\tprintf(\"dlsym: %s\\n\", dlerror());\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t}\n    }\n    if (ion_handle == NULL) {\n        ion_handle = handle;\n    }\n\treturn duss_hal_mem_alloc2(handle, mem_handle, size, param1, param2, param3);\n}\n\nstatic duss_result_t (*duss_hal_display_aquire_plane2)(duss_disp_instance_handle_t * , duss_disp_plane_type_t , duss_disp_plane_id_t * ) = 0;\n\n// Patch libduml_hal's duss_hal_display_aquire_plane.\n// Use the first invocation of this with plane == 5 to steal the duss_hal_instance_handle_t pointing to the display driver.\nduss_result_t duss_hal_display_aquire_plane(duss_disp_instance_handle_t *disp, duss_disp_plane_type_t plane_type, duss_disp_plane_id_t *plane_id) {\n\tif (duss_hal_display_aquire_plane2 == NULL) {\n\t    duss_hal_display_aquire_plane2 = dlsym(RTLD_NEXT, \"duss_hal_display_aquire_plane\");\n\t\tif (duss_hal_display_aquire_plane2 == 0){\n\t\t\tif(hal_lib == NULL) {\n\t\t\t\thal_lib = dlopen(\"/system/lib/libduml_hal.so\", 1);\n\t\t\t}\n\t\t\tduss_hal_display_aquire_plane2 = dlsym(hal_lib, \"duss_hal_display_aquire_plane\");\n\t\t\tif (duss_hal_display_aquire_plane2 == 0) {\n\t\t\t\tprintf(\"dlsym: %s\\n\", dlerror());\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t}\n    }\n\tif(disp_instance == NULL && *plane_id == 5) {\n\t\tdisp_instance = disp;\n\t}\n\treturn duss_hal_display_aquire_plane2(disp, plane_type, plane_id);\n}\n\nduss_result_t (*duss_hal_display_plane_blending_set2)(duss_disp_instance_handle_t *disp, duss_disp_plane_id_t plane_id, duss_disp_plane_blending_t *blending) = NULL;\nduss_result_t duss_hal_display_plane_blending_set(duss_disp_instance_handle_t *disp, duss_disp_plane_id_t plane_id, duss_disp_plane_blending_t *blending) {\n    if (duss_hal_display_plane_blending_set2 == NULL) {\n\t   duss_hal_display_plane_blending_set2 = dlsym(RTLD_NEXT, \"duss_hal_display_plane_blending_set\");\n\t\tif (duss_hal_display_plane_blending_set2 == NULL) {\n\t\t\tif (hal_lib == NULL) {\n\t\t\t\thal_lib = dlopen(\"/system/lib/libduml_hal.so\", 1);\n\t\t\t}\n\t\t\tduss_hal_display_plane_blending_set2 = dlsym(hal_lib, \"duss_hal_display_plane_blending_set\");\n\t\t\tif (duss_hal_display_plane_blending_set2 == NULL) {\n\t\t\t\tprintf(\"dlsym: %s\\n\", dlerror());\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t}\n    }\n    // Patch blending order for DJI UI on V1 Goggles to match V2, so we can draw under them.\n    if (blending->order == 1) {\n        blending->order = 4;\n    }\n    return duss_hal_display_plane_blending_set2(disp, plane_id, blending);\n}"
  },
  {
    "path": "jni/fakehd/fakehd.c",
    "content": "#include <stdio.h>\n#include <string.h>\n#include <stdlib.h>\n\n#include \"json/osd_config.h\"\n#include \"toast/toast.h\"\n\n#define FAKEHD_ENABLE_KEY \"fakehd_enable\"\n#define FAKEHD_LOCK_CENTER_KEY \"fakehd_lock_center\"\n#define FAKEHD_HIDE_THROTTLE_KEY \"fakehd_hide_throttle_element\" // for compat\n#define FAKEHD_MENU_SWITCH_KEY \"fakehd_menu_switch\"\n#define FAKEHD_HIDE_MENU_SWITCH_KEY \"fakehd_hide_menu_switch\" // for compat\n#define FAKEHD_LAYOUT_DEBUG_KEY \"fakehd_layout_debug\"\n#define FAKEHD_COLUMNS_KEY \"fakehd_columns\"\n#define FAKEHD_ROWS_KEY \"fakehd_rows\"\n\n#define INPUT_ROWS 16\n#define INPUT_COLS 30\n\nint fakehd_enabled = 0;\nstatic int fakehd_hide_menu_switch = 0;\nstatic int fakehd_lock_center = 0;\nstatic int fakehd_layout_debug = 0;\nstatic int fakehd_menu_switch_char = 4; // betaflight throttle icon\nstatic int fakehd_trigger_x = 99;\nstatic int fakehd_trigger_y = 99;\nstatic char fakehd_columns = 'S';\nstatic char fakehd_rows[INPUT_COLS] = \"WWWWWWCCWWWWWWWD\";\n\n#ifdef DEBUG\n#define DEBUG_PRINT(fmt, args...) fprintf(stderr, fmt, ##args)\n#else\n#define DEBUG_PRINT(fmt, args...)\n#endif\n\nvoid load_fakehd_config()\n{\n    DEBUG_PRINT(\"checking for fakehd enabled\\n\");\n    if (get_boolean_config_value(FAKEHD_ENABLE_KEY))\n    {\n        DEBUG_PRINT(\"fakehd enabled\\n\");\n        toast(\"FAKEHD ENABLED\");\n\n        fakehd_enabled = 1;\n    }\n    else\n    {\n        DEBUG_PRINT(\"fakehd disabled\\n\");\n    }\n\n    DEBUG_PRINT(\"checking for fakehd layout debug\\n\");\n    if (get_boolean_config_value(FAKEHD_LAYOUT_DEBUG_KEY))\n    {\n        DEBUG_PRINT(\"fakehd layout debug\\n\");\n        fakehd_layout_debug = 1;\n    }\n    else\n    {\n        DEBUG_PRINT(\"fakehd layout debug off\\n\");\n    }\n\n    DEBUG_PRINT(\"checking for fakehd hide throttle \\n\");\n    if (get_boolean_config_value(FAKEHD_HIDE_MENU_SWITCH_KEY))\n    {\n        DEBUG_PRINT(\"fakehd hide throttle\\n\");\n        fakehd_hide_menu_switch = 1;\n    }\n    else\n    {\n        DEBUG_PRINT(\"fakehd no hide throttle\\n\");\n    }\n    DEBUG_PRINT(\"checking for fakehd lock center \\n\");\n    if (get_boolean_config_value(FAKEHD_LOCK_CENTER_KEY))\n    {\n        DEBUG_PRINT(\"fakehd lock center\\n\");\n        fakehd_lock_center = 1;\n    }\n    else\n    {\n        DEBUG_PRINT(\"fakehd no lock center\\n\");\n    }\n\n    int trigger = get_integer_config_value(FAKEHD_MENU_SWITCH_KEY);\n    if (trigger)\n    {\n        DEBUG_PRINT(\"fakehd found custom trigger\\n\");\n        fakehd_menu_switch_char = trigger;\n        toast(\"FHD MENU SWITCH %c\", 4);\n    }\n    // trigger\n    // rows\n    const char * rows = get_string_config_value(FAKEHD_ROWS_KEY);\n    if (rows) {\n        DEBUG_PRINT(\"fakehd found custom row conf\\n\");\n        memcpy(fakehd_rows, rows, INPUT_COLS);\n    }\n\n    const char * cols = get_string_config_value(FAKEHD_COLUMNS_KEY);\n    if (cols)\n    {\n        DEBUG_PRINT(\"fakehd found col conf\\n\");\n        fakehd_columns = cols[0];\n    }\n    DEBUG_PRINT(\"fakehd finished config init\\n\");\n}\n\nstatic void fakehd_get_column_config(int cols[INPUT_ROWS])\n{\n    switch (fakehd_columns)\n    {\n    case 'T':\n        memcpy(cols, (int[]){0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, sizeof(cols[0]) * INPUT_ROWS);\n        break;\n    case 'M':\n        memcpy(cols, (int[]){3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18}, sizeof(cols[0]) * INPUT_ROWS);\n        break;\n    case 'B':\n        memcpy(cols, (int[]){6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21}, sizeof(cols[0]) * INPUT_ROWS);\n        break;\n    case 'S':\n    default:\n        memcpy(cols, (int[]){0, 1, 2, 3, 4, 8, 9, 10, 11, 12, 16, 17, 18, 19, 20, 21}, sizeof(cols[0]) * INPUT_ROWS);\n        break;\n    }\n\n    // If more flexibility needed / when config allows - I suggest the 'default' switch block is separated\n    // and used to lookup the mapping from the config file, letting the user define extras?\n\n}\n\nstatic void fakehd_get_row_config(int rownum, int row[INPUT_COLS])\n{\n    char rowmode = fakehd_rows[rownum];\n    switch (rowmode)\n    {\n    case 'L':\n        memcpy(row, (int[]){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}, sizeof(row[0]) * INPUT_COLS);\n        break;\n    case 'C':\n        memcpy(row, (int[]){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}, sizeof(row[0]) * INPUT_COLS);\n        break;\n    case 'R':\n        memcpy(row, (int[]){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}, sizeof(row[0]) * INPUT_COLS);\n        break;\n    case 'T':\n        memcpy(row, (int[]){0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59}, sizeof(row[0]) * INPUT_COLS);\n        break;\n    case 'F':\n        memcpy(row, (int[]){10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49}, sizeof(row[0]) * INPUT_COLS);\n        break;\n    case 'D':\n        memcpy(row, (int[]){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}, sizeof(row[0]) * INPUT_COLS);\n        break;\n    case 'W':\n    default:\n        memcpy(row, (int[]){0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59}, sizeof(row[0]) * INPUT_COLS);\n        break;\n    }\n\n    // If more flexibility needed / when config allows - I suggest the 'default' switch block is separated\n    // and used to lookup the mapping from the config file, letting the user define extras?\n}\n\n\n// when possible, this should be called on reconnect. it will do what's needed to put fakehd back\n// into fresh booted state\nvoid fakehd_reset() {\n    // clear saved centering trigger position\n    int fakehd_trigger_x = 99;\n    int fakehd_trigger_y = 99;\n}\n\nvoid fakehd_enable()\n{\n    fakehd_enabled = 1;\n}\n\nvoid fakehd_disable()\n{\n    fakehd_enabled = 0;\n    fakehd_reset();\n}\n\nint fakehd_is_enabled() {\n    return fakehd_enabled;\n}\n\nvoid fakehd_map_sd_character_map_to_hd(uint16_t sd_character_map[60][22], uint16_t hd_character_map[60][22])\n{\n    int row[INPUT_COLS];\n    int col[INPUT_ROWS];\n\n    fakehd_get_column_config(col);\n\n    int render_x = 0;\n    int render_y = 0;\n    for (int y = INPUT_ROWS-1; y >= 0; y--)\n    {\n        fakehd_get_row_config(y, row);\n        for (int x = INPUT_COLS-1; x >= 0; x--)\n        {\n            // to visualise the layout better in dev\n            if (fakehd_layout_debug && sd_character_map[x][y] == 0) {\n                sd_character_map[x][y] = 48 + (x % 10);\n            }\n\n            // skip if it's not a character\n            if (sd_character_map[x][y] != 0)\n            {\n                // if current element is fly min or throttle icon\n                // record the current position as the 'trigger' position\n                if (fakehd_trigger_x == 99 && sd_character_map[x][y] == fakehd_menu_switch_char)\n                {\n                    DEBUG_PRINT(\"found fakehd triggger \\n\");\n                    fakehd_trigger_x = x;\n                    fakehd_trigger_y = y;\n                }\n\n                // if we have seen a trigger (see above) - and it's now gone, switch to centering\n                // this is intented to center the menu + postflight stats, which don't contain\n                // timer/battery symbols\n                if (\n                    fakehd_lock_center ||\n                    (fakehd_trigger_x != 99 &&\n                     sd_character_map[fakehd_trigger_x][fakehd_trigger_y] != fakehd_menu_switch_char))\n                {\n                    render_x = x + 15;\n                    render_y = y + 3;\n                }\n                else\n                {\n                    render_y = col[y];\n                    render_x = row[x];\n                }\n                // 0 out the throttle element if configured to do so\n                // and also the three adjacent positions where the thottle percent will be\n                if (fakehd_trigger_x != 99 &&\n                    fakehd_hide_menu_switch &&\n                    sd_character_map[x][y] == fakehd_menu_switch_char)\n                {\n                    hd_character_map[render_x][render_y] = 0;\n                    (render_x <= 57) && (hd_character_map[render_x + 1][render_y] = 0);\n                    (render_x <= 56) && (hd_character_map[render_x + 2][render_y] = 0);\n                    (render_x <= 55) && (hd_character_map[render_x + 3][render_y] = 0);\n                    (render_x <= 54) && (hd_character_map[render_x + 4][render_y] = 0);\n                    (render_x <= 53) && (hd_character_map[render_x + 5][render_y] = 0);\n                }\n                else\n                {\n                    // otherwise, the normal path\n                    hd_character_map[render_x][render_y] = sd_character_map[x][y];\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "jni/fakehd/fakehd.h",
    "content": "#pragma once\n\nvoid load_fakehd_config();\nvoid fakehd_disable();\nvoid fakehd_enable();\nint fakehd_is_enabled();\nvoid fakehd_reset();\nvoid fakehd_map_sd_character_map_to_hd(uint16_t sd_character_map[60][22], uint16_t hd_character_map[60][22]);"
  },
  {
    "path": "jni/font/font.c",
    "content": "#include <sys/fcntl.h>\n#include <sys/mman.h>\n#include <sys/stat.h>\n#include <string.h>\n#include <stdlib.h>\n#include <unistd.h>\n// #include <ctype.h>\n\n#include \"../libspng/spng.h\"\n#include \"font.h\"\n#include \"../util/debug.h\"\n\n#define BYTES_PER_PIXEL 4\n#define HD_FONT_WIDTH 24\n\n/* Font helper methods */\n\nvoid get_font_path_with_extension(char *font_path_dest, const char *font_path, const char *extension, uint8_t len, uint8_t is_hd, const char *font_variant)\n{\n    char name_buf[len];\n    char res_buf[len];\n\n    if (font_variant != NULL && strlen(font_variant) > 0)\n    {\n        snprintf(name_buf, len, \"%s_%s\", font_path, font_variant);\n    } else {\n        snprintf(name_buf, len, \"%s\", font_path);\n    }\n\n    if (is_hd)\n    {\n        // surely there's a better way...\n        snprintf(res_buf, len, \"%s\", \"_hd\");\n    } else {\n        snprintf(res_buf, len, \"%s\", \"\");\n    }\n    snprintf(font_path_dest, len, \"%s%s%s\", name_buf, res_buf, extension);\n    DEBUG_PRINT(\"Font path: %s\\n\", font_path_dest);\n}\n\nstatic int open_font(const char *filename, display_info_t *display_info, const char *font_variant)\n{\n    char file_path[255];\n    int is_hd = (display_info->font_width == HD_FONT_WIDTH) ? 1 : 0;\n    get_font_path_with_extension(file_path, filename, \".png\", 255, is_hd, font_variant);\n    DEBUG_PRINT(\"Opening font: %s\\n\", file_path);\n    struct stat st;\n    memset(&st, 0, sizeof(st));\n    stat(file_path, &st);\n    size_t filesize = st.st_size;\n    if(!(filesize > 0)) {\n        DEBUG_PRINT(\"Font file did not exist: %s\\n\", file_path);\n        return -1;\n    }\n\n    FILE *fd = fopen(file_path, \"rb\");\n    if (!fd) {\n        DEBUG_PRINT(\"Could not open file %s\\n\", file_path);\n        return -1;\n    }\n\n    spng_ctx *ctx = spng_ctx_new(0);\n    DEBUG_PRINT(\"Allocated PNG context\\n\");\n    // Set some kind of reasonable PNG limit so we don't get blown up\n    size_t limit = 1024 * 1024 * 64;\n    spng_set_chunk_limits(ctx, limit, limit);\n    DEBUG_PRINT(\"Set PNG chunk limits\\n\");\n    spng_set_png_file(ctx, fd);\n    DEBUG_PRINT(\"Set PNG file\\n\");\n\n    struct spng_ihdr ihdr;\n    int ret = spng_get_ihdr(ctx, &ihdr);\n    DEBUG_PRINT(\"Got PNG header\\n\");\n\n    if(ret)\n    {\n        printf(\"spng_get_ihdr() error: %s\\n\", spng_strerror(ret));\n        goto err;\n    }\n\n    if(ihdr.height != display_info->font_height * NUM_CHARS) {\n        printf(\"font invalid height, got %d wanted %d\\n\", ihdr.height, display_info->font_height * NUM_CHARS);\n        goto err;\n    }\n\n    if(ihdr.width % display_info->font_width != 0) {\n        printf(\"font invalid width, not a multiple of %d\\n\", display_info->font_width);\n        goto err;\n    }\n\n    DEBUG_PRINT(\"Image pixel size %d x %d\\n\", ihdr.width, ihdr.height);\n\n    int num_pages = ihdr.width / display_info->font_width;\n\n    DEBUG_PRINT(\"Font has %d pages\\n\", num_pages);\n\n    size_t image_size = 0;\n    int fmt = SPNG_FMT_RGBA8;\n    ret = spng_decoded_image_size(ctx, fmt, &image_size);\n    if(ret) {\n        goto err;\n    }\n\n    DEBUG_PRINT(\"Allocating image size %d\\n\", image_size);\n\n    void* font_data = malloc(image_size);\n    ret = spng_decode_image(ctx, font_data, image_size, SPNG_FMT_RGBA8, 0);\n    if(ret) {\n        printf(\"Failed to decode PNG!\\n\");\n        free(font_data);\n        goto err;\n    }\n\n    for(int page = 0; page < num_pages; page++) {\n        DEBUG_PRINT(\"Loading font page %d of %d, placing %x\\n\", page, num_pages, display_info->fonts);\n        display_info->fonts[page] = malloc(display_info->font_width * display_info->font_height * NUM_CHARS * BYTES_PER_PIXEL);\n        DEBUG_PRINT(\"Allocated %d bytes for font page buf at%x\\n\", display_info->font_width * display_info->font_height * NUM_CHARS * BYTES_PER_PIXEL, display_info->fonts[page]);\n        for(int char_num = 0; char_num < NUM_CHARS; char_num++) {\n            for(int y = 0; y < display_info->font_height; y++) {\n                // Copy each character line at a time into the correct font buffer\n                int char_width_bytes = display_info->font_width * BYTES_PER_PIXEL;\n                int char_size_bytes_dest = (display_info->font_width * display_info->font_height * BYTES_PER_PIXEL);\n                int char_size_bytes_src =  (ihdr.width * display_info->font_height * BYTES_PER_PIXEL);\n                memcpy((uint8_t *)display_info->fonts[page] + (char_num * char_size_bytes_dest) + (y * char_width_bytes), (uint8_t *)font_data + (char_num * char_size_bytes_src) + (ihdr.width * y * BYTES_PER_PIXEL) + (page * char_width_bytes), char_width_bytes);\n            }\n        }\n    }\n\n    free(font_data);\n    spng_ctx_free(ctx);\n    fclose(fd);\n    return 0;\n    err:\n        spng_ctx_free(ctx);\n        fclose(fd);\n        return -1;\n}\n\nvoid load_font(display_info_t *display_info, const char *font_variant) {\n\n    // Note: load_font will not replace an existing font.\n    if(display_info->fonts[0] == NULL) {\n        int loaded_font = 0;\n        DEBUG_PRINT(\"IN LOAD_FONT\\n\");\n        // create a copy of font_variant\n        char font_variant_lower[5] = \"\";\n        if (font_variant != NULL)\n        {\n            DEBUG_PRINT(\"Lowercasing variant\\n\");\n            size_t length = strlen(font_variant);\n            for (size_t i = 0; i < length && i < 4; i++) // Ensure not to exceed array bounds\n            {\n                font_variant_lower[i] = tolower(font_variant[i]);\n            }\n        }\n        else\n        {\n            DEBUG_PRINT(\"Font variant is NULL\\n\");\n        }\n\n        DEBUG_PRINT(\"Loading font %s\\n\", font_variant_lower);\n\n        char *fallback_font_variant = \"\";\n        if (strcmp(font_variant_lower, \"btfl\") == 0)\n        {\n            DEBUG_PRINT(\"Setting fallback font variant to bf\\n\");\n            fallback_font_variant = \"bf\";\n        }\n        else if (strcmp(font_variant_lower, \"ultr\") == 0)\n        {\n            DEBUG_PRINT(\"Setting fallback font variant to ultra\\n\");\n            fallback_font_variant = \"ultra\";\n        }\n\n        // try the three paths for the current font\n        DEBUG_PRINT(\"Loading from: %s %s\\n\", SDCARD_FONT_PATH, font_variant_lower);\n        loaded_font = open_font(SDCARD_FONT_PATH, display_info, font_variant_lower);\n        if (loaded_font < 0 && strcmp(fallback_font_variant, \"\") != 0)\n        {\n            DEBUG_PRINT(\"Loading fallback variant from: %s %s\\n\", SDCARD_FONT_PATH, fallback_font_variant);\n            loaded_font = open_font(SDCARD_FONT_PATH, display_info, fallback_font_variant);\n        }\n        if (loaded_font < 0)\n        {\n            DEBUG_PRINT(\"Loading from: %s %s\\n\", FALLBACK_FONT_PATH, font_variant_lower);\n            loaded_font = open_font(FALLBACK_FONT_PATH, display_info, font_variant_lower);\n        }\n        if (loaded_font < 0 && strcmp(fallback_font_variant, \"\") != 0)\n        {\n            DEBUG_PRINT(\"Loading fallback variant from: %s %s\\n\", FALLBACK_FONT_PATH, fallback_font_variant);\n            loaded_font = open_font(FALLBACK_FONT_PATH, display_info, fallback_font_variant);\n        }\n        if (loaded_font < 0)\n        {\n            DEBUG_PRINT(\"Loading from: %s %s\\n\", ENTWARE_FONT_PATH, font_variant_lower);\n            loaded_font = open_font(ENTWARE_FONT_PATH, display_info, font_variant_lower);\n        }\n\n\n        // finally, if we have no fonts for this FC, fallback to the default font\n        if (loaded_font)\n        {\n            DEBUG_PRINT(\"Loading generic from: %s\\n\", SDCARD_FONT_PATH);\n            loaded_font = open_font(SDCARD_FONT_PATH, display_info, \"\");\n            if (loaded_font < 0)\n            {\n                DEBUG_PRINT(\"Loading generic from: %s\\n\", FALLBACK_FONT_PATH);\n                loaded_font = open_font(FALLBACK_FONT_PATH, display_info, \"\");\n            }\n            if (loaded_font < 0)\n            {\n                DEBUG_PRINT(\"Loading generic from: %s\\n\", ENTWARE_FONT_PATH);\n                loaded_font = open_font(ENTWARE_FONT_PATH, display_info, \"\");\n            }\n        }\n    }\n}\n\nvoid close_font(display_info_t *display_info) {\n    for(int i = 0; i < NUM_FONT_PAGES; i++) {\n        if(display_info->fonts[i] != NULL) {\n            free(display_info->fonts[i]);\n            display_info->fonts[i] = NULL;\n        }\n    }\n}\n\n"
  },
  {
    "path": "jni/font/font.h",
    "content": "#pragma once\n\n#include <stdint.h>\n\n#include \"../util/display_info.h\"\n\n#define FALLBACK_FONT_PATH \"/blackbox/font\"\n#define ENTWARE_FONT_PATH \"/opt/fonts/font\"\n#define SDCARD_FONT_PATH \"/storage/sdcard0/font\"\n\n#define FALLBACK_FONT NULL\n\ntypedef enum\n{\n    FONT_VARIANT_GENERIC,\n    FONT_VARIANT_BETAFLIGHT,\n    FONT_VARIANT_INAV,\n    FONT_VARIANT_ARDUPILOT,\n    FONT_VARIANT_KISS_ULTRA,\n    FONT_VARIANT_QUICKSILVER,\n    FONT_VARIANT_COUNT\n} font_variant_e;\n\nvoid load_font(display_info_t *display_info, const char *font_variant);\nvoid close_font(display_info_t *display_info);\nvoid get_font_path_with_extension(char *font_path_dest, const char *font_path, const char *extension, uint8_t len, uint8_t is_hd, const char *font_variant);\n"
  },
  {
    "path": "jni/hw/dji_display.c",
    "content": "#include <stdlib.h>\n#include \"dji_display.h\"\n#include \"util/debug.h\"\n\n#define GOGGLES_V1_VOFFSET 575\n#define GOGGLES_V2_VOFFSET 215\n\ndji_display_state_t *dji_display_state_alloc(uint8_t is_v2_goggles) {\n    dji_display_state_t *display_state = calloc(1, sizeof(dji_display_state_t));\n    display_state->disp_instance_handle = (duss_disp_instance_handle_t *)calloc(1, sizeof(duss_disp_instance_handle_t));\n    display_state->fb_0 = (duss_frame_buffer_t *)calloc(1,sizeof(duss_frame_buffer_t));\n    display_state->fb_1 = (duss_frame_buffer_t *)calloc(1,sizeof(duss_frame_buffer_t));\n    display_state->pb_0 = (duss_disp_plane_blending_t *)calloc(1, sizeof(duss_disp_plane_blending_t));\n    display_state->is_v2_goggles = is_v2_goggles;\n    display_state->frame_drawn = 0;\n    return display_state;\n}\n\nvoid dji_display_state_free(dji_display_state_t *display_state) {\n    free(display_state->disp_instance_handle);\n    free(display_state->fb_0);\n    free(display_state->fb_1);\n    free(display_state->pb_0);\n    free(display_state);\n}\n\nvoid dji_display_close_framebuffer(dji_display_state_t *display_state) {\n    duss_hal_display_port_enable(display_state->disp_instance_handle, 3, 0);\n    duss_hal_display_release_plane(display_state->disp_instance_handle, display_state->plane_id);\n    duss_hal_display_close(display_state->disp_handle, &display_state->disp_instance_handle);\n    duss_hal_mem_free(display_state->ion_buf_0);\n    duss_hal_mem_free(display_state->ion_buf_1);\n    duss_hal_device_close(display_state->disp_handle);\n    duss_hal_device_stop(display_state->ion_handle);\n    duss_hal_device_close(display_state->ion_handle);\n    duss_hal_deinitialize();\n}\n\nvoid dji_display_open_framebuffer(dji_display_state_t *display_state, duss_disp_plane_id_t plane_id) {\n    uint32_t hal_device_open_unk = 0;\n    duss_result_t res = 0;\n\n    display_state->plane_id = plane_id;\n\n    // PLANE BLENDING\n\n    display_state->pb_0->is_enable = 1;\n    display_state->pb_0->voffset = GOGGLES_V1_VOFFSET; // TODO just check hwid to figure this out\n    display_state->pb_0->hoffset = 0;\n    display_state->pb_0->order = 2;\n\n    // Global alpha - disable as we want per pixel alpha.\n\n    display_state->pb_0->glb_alpha_en = 0;\n    display_state->pb_0->glb_alpha_val = 0;\n\n    // Blending algorithm 1 seems to work.\n\n    display_state->pb_0->blending_alg = 1;\n\n    duss_hal_device_desc_t device_descs[3] = {\n        {\"/dev/dji_display\", &duss_hal_attach_disp, &duss_hal_detach_disp, 0x0},\n        {\"/dev/ion\", &duss_hal_attach_ion_mem, &duss_hal_detach_ion_mem, 0x0},\n        {0,0,0,0}\n    };\n\n    duss_hal_initialize(device_descs);\n\n    res = duss_hal_device_open(\"/dev/dji_display\",&hal_device_open_unk,&display_state->disp_handle);\n    if (res != 0) {\n        printf(\"failed to open dji_display device\");\n        exit(0);\n    }\n    res = duss_hal_display_open(display_state->disp_handle, &display_state->disp_instance_handle, 0);\n    if (res != 0) {\n        printf(\"failed to open display hal\");\n        exit(0);\n    }\n\n    res = duss_hal_display_reset(display_state->disp_instance_handle);\n    if (res != 0) {\n        printf(\"failed to reset display\");\n        exit(0);\n    }\n\n    // No idea what this \"plane mode\" actually does but it's different on V2\n    uint8_t acquire_plane_mode = display_state->is_v2_goggles ? 6 : 0;\n\n    res = duss_hal_display_aquire_plane(display_state->disp_instance_handle,acquire_plane_mode,&plane_id);\n    if (res != 0) {\n        printf(\"failed to acquire plane\");\n        exit(0);\n    }\n    res = duss_hal_display_port_enable(display_state->disp_instance_handle, 3, 1);\n    if (res != 0) {\n        printf(\"failed to enable display port\");\n        exit(0);\n    }\n\n    res = duss_hal_display_plane_blending_set(display_state->disp_instance_handle, plane_id, display_state->pb_0);\n\n    if (res != 0) {\n        printf(\"failed to set blending\");\n        exit(0);\n    }\n    res = duss_hal_device_open(\"/dev/ion\", &hal_device_open_unk, &display_state->ion_handle);\n    if (res != 0) {\n        printf(\"failed to open shared VRAM\");\n        exit(0);\n    }\n    res = duss_hal_device_start(display_state->ion_handle,0);\n    if (res != 0) {\n        printf(\"failed to start VRAM device\");\n        exit(0);\n    }\n\n    res = duss_hal_mem_alloc(display_state->ion_handle,&display_state->ion_buf_0,0x473100,0x400,0,0x17);\n    if (res != 0) {\n        printf(\"failed to allocate VRAM\");\n        exit(0);\n    }\n    res = duss_hal_mem_map(display_state->ion_buf_0, &display_state->fb0_virtual_addr);\n    if (res != 0) {\n        printf(\"failed to map VRAM\");\n        exit(0);\n    }\n    res = duss_hal_mem_get_phys_addr(display_state->ion_buf_0, &display_state->fb0_physical_addr);\n    if (res != 0) {\n        printf(\"failed to get FB0 phys addr\");\n        exit(0);\n    }\n    printf(\"first buffer VRAM mapped virtual memory is at %p : %p\\n\", display_state->fb0_virtual_addr, display_state->fb0_physical_addr);\n\n    res = duss_hal_mem_alloc(display_state->ion_handle,&display_state->ion_buf_1,0x473100,0x400,0,0x17);\n    if (res != 0) {\n        printf(\"failed to allocate FB1 VRAM\");\n        exit(0);\n    }\n    res = duss_hal_mem_map(display_state->ion_buf_1,&display_state->fb1_virtual_addr);\n    if (res != 0) {\n        printf(\"failed to map FB1 VRAM\");\n        exit(0);\n    }\n    res = duss_hal_mem_get_phys_addr(display_state->ion_buf_1, &display_state->fb1_physical_addr);\n    if (res != 0) {\n        printf(\"failed to get FB1 phys addr\");\n        exit(0);\n    }\n    printf(\"second buffer VRAM mapped virtual memory is at %p : %p\\n\", display_state->fb1_virtual_addr, display_state->fb1_physical_addr);\n\n    for(int i = 0; i < 2; i++) {\n        duss_frame_buffer_t *fb = i ? display_state->fb_1 : display_state->fb_0;\n        fb->buffer = i ? display_state->ion_buf_1 : display_state->ion_buf_0;\n        fb->pixel_format = display_state->is_v2_goggles ? DUSS_PIXFMT_RGBA8888_GOGGLES_V2 : DUSS_PIXFMT_RGBA8888; // 20012 instead on V2\n        fb->frame_id = i;\n        fb->planes[0].bytes_per_line = 0x1680;\n        fb->planes[0].offset = 0;\n        fb->planes[0].plane_height = 810;\n        fb->planes[0].bytes_written = 0x473100;\n        fb->width = 1440;\n        fb->height = 810;\n        fb->plane_count = 1;\n    }\n}\n\n\nvoid dji_display_open_framebuffer_injected(dji_display_state_t *display_state, duss_disp_instance_handle_t *disp, duss_hal_obj_handle_t ion_handle, duss_disp_plane_id_t plane_id) {\n    uint32_t hal_device_open_unk = 0;\n    duss_result_t res = 0;\n    display_state->disp_instance_handle = disp;\n    display_state->ion_handle = ion_handle;\n    display_state->plane_id = plane_id;\n\n    // PLANE BLENDING\n\n    display_state->pb_0->is_enable = 1;\n\n    // TODO just check hwid to figure this out. Not actually V1/V2 related but an HW version ID.\n\n    display_state->pb_0->voffset = GOGGLES_V1_VOFFSET;\n    display_state->pb_0->hoffset = 0;\n\n    // On Goggles V1, the UI and video are in Z-Order 1.\n    // On Goggles V2, they're in Z-Order 4, but we inline patch them to Z-Order 1 (see displayport_osd_shim.c)\n\n    display_state->pb_0->order = 2;\n\n    // Global alpha - disable as we want per pixel alpha.\n\n    display_state->pb_0->glb_alpha_en = 0;\n    display_state->pb_0->glb_alpha_val = 0;\n\n    // These aren't documented. Blending algorithm 0 is employed for menus and 1 for screensaver.\n\n    display_state->pb_0->blending_alg = 1;\n\n    // No idea what this \"plane mode\" actually does but it's different on V2\n    uint8_t acquire_plane_mode = display_state->is_v2_goggles ? 6 : 0;\n\n    DEBUG_PRINT(\"acquire plane\\n\");\n    res = duss_hal_display_aquire_plane(display_state->disp_instance_handle,acquire_plane_mode,&plane_id);\n    if (res != 0) {\n        DEBUG_PRINT(\"failed to acquire plane\");\n        exit(0);\n    }\n\n    res = duss_hal_display_plane_blending_set(display_state->disp_instance_handle, plane_id, display_state->pb_0);\n\n    if (res != 0) {\n        DEBUG_PRINT(\"failed to set blending\");\n        exit(0);\n    }\n    DEBUG_PRINT(\"alloc ion buf\\n\");\n    res = duss_hal_mem_alloc(display_state->ion_handle,&display_state->ion_buf_0,0x473100,0x400,0,0x17);\n    if (res != 0) {\n        DEBUG_PRINT(\"failed to allocate VRAM\");\n        exit(0);\n    }\n    res = duss_hal_mem_map(display_state->ion_buf_0, &display_state->fb0_virtual_addr);\n    if (res != 0) {\n        DEBUG_PRINT(\"failed to map VRAM\");\n        exit(0);\n    }\n    res = duss_hal_mem_get_phys_addr(display_state->ion_buf_0, &display_state->fb0_physical_addr);\n    if (res != 0) {\n        DEBUG_PRINT(\"failed to get FB0 phys addr\");\n        exit(0);\n    }\n    DEBUG_PRINT(\"first buffer VRAM mapped virtual memory is at %p : %p\\n\", display_state->fb0_virtual_addr, display_state->fb0_physical_addr);\n\n    res = duss_hal_mem_alloc(display_state->ion_handle,&display_state->ion_buf_1,0x473100,0x400,0,0x17);\n    if (res != 0) {\n        DEBUG_PRINT(\"failed to allocate FB1 VRAM\");\n        exit(0);\n    }\n    res = duss_hal_mem_map(display_state->ion_buf_1,&display_state->fb1_virtual_addr);\n    if (res != 0) {\n        DEBUG_PRINT(\"failed to map FB1 VRAM\");\n        exit(0);\n    }\n    res = duss_hal_mem_get_phys_addr(display_state->ion_buf_1, &display_state->fb1_physical_addr);\n    if (res != 0) {\n        DEBUG_PRINT(\"failed to get FB1 phys addr\");\n        exit(0);\n    }\n    DEBUG_PRINT(\"second buffer VRAM mapped virtual memory is at %p : %p\\n\", display_state->fb1_virtual_addr, display_state->fb1_physical_addr);\n\n    for(int i = 0; i < 2; i++) {\n        duss_frame_buffer_t *fb = i ? display_state->fb_1 : display_state->fb_0;\n        fb->buffer = i ? display_state->ion_buf_1 : display_state->ion_buf_0;\n        fb->pixel_format = display_state->is_v2_goggles ? DUSS_PIXFMT_RGBA8888_GOGGLES_V2 : DUSS_PIXFMT_RGBA8888; // 20012 instead on V2\n        fb->frame_id = i;\n        fb->planes[0].bytes_per_line = 0x1680;\n        fb->planes[0].offset = 0;\n        fb->planes[0].plane_height = 810;\n        fb->planes[0].bytes_written = 0x473100;\n        fb->width = 1440;\n        fb->height = 810;\n        fb->plane_count = 1;\n    }\n}\n\nvoid dji_display_push_frame(dji_display_state_t *display_state) {\n    if (display_state->frame_drawn == 0) {\n        duss_frame_buffer_t *fb = display_state->fb_0;\n        duss_hal_mem_sync(fb->buffer, 1);\n        display_state->frame_drawn = 1;\n        printf(\"fbdebug pushing frame\\n\");\n        duss_hal_display_push_frame(display_state->disp_instance_handle, display_state->plane_id, fb);\n    } else {\n        DEBUG_PRINT(\"!!! Dropped frame due to pending frame push!\\n\");\n    }\n    memcpy(display_state->fb0_virtual_addr, display_state->fb1_virtual_addr, sizeof(uint32_t) * 1440 * 810);\n}\n\nvoid *dji_display_get_fb_address(dji_display_state_t *display_state) {\n     return display_state->fb1_virtual_addr;\n}\n\n"
  },
  {
    "path": "jni/hw/dji_display.h",
    "content": "#pragma once\n#include \"duml_hal.h\"\n\ntypedef struct dji_display_state_s {\n    duss_disp_plane_id_t plane_id;\n    duss_hal_obj_handle_t disp_handle;\n    duss_hal_obj_handle_t ion_handle;\n    duss_disp_vop_id_t vop_id;\n    duss_hal_mem_handle_t ion_buf_0;\n    duss_hal_mem_handle_t ion_buf_1;\n    void * fb0_virtual_addr;\n    void * fb0_physical_addr;\n    void * fb1_virtual_addr;\n    void * fb1_physical_addr;\n    duss_disp_instance_handle_t *disp_instance_handle;\n    duss_frame_buffer_t *fb_0;\n    duss_frame_buffer_t *fb_1;\n    duss_disp_plane_blending_t *pb_0;\n    uint8_t is_v2_goggles;\n    uint8_t frame_drawn;\n} dji_display_state_t;\n\nvoid dji_display_push_frame(dji_display_state_t *display_state);\nvoid dji_display_open_framebuffer(dji_display_state_t *display_state, duss_disp_plane_id_t plane_id);\nvoid dji_display_open_framebuffer_injected(dji_display_state_t *display_state, duss_disp_instance_handle_t *disp, duss_hal_obj_handle_t ion_handle, duss_disp_plane_id_t plane_id);\nvoid dji_display_close_framebuffer(dji_display_state_t *display_state);\ndji_display_state_t *dji_display_state_alloc(uint8_t is_v2_goggles);\nvoid dji_display_state_free(dji_display_state_t *display_state);\nvoid *dji_display_get_fb_address(dji_display_state_t *display_state);\n"
  },
  {
    "path": "jni/hw/dji_radio_shm.c",
    "content": "#include <assert.h>\n#include <stdio.h>\n#include <fcntl.h>\n#include <sys/mman.h>\n\n#include \"dji_radio_shm.h\"\n\nvoid open_dji_radio_shm(dji_shm_state_t *shm) {\n    int fd = open(\"/dev/mem\", O_RDWR);\n    assert(fd > 0);\n    shm->mapped_address = mmap64(NULL, RTOS_SHM_SIZE, PROT_READ, MAP_SHARED, fd, RTOS_SHM_ADDRESS);\n    assert(shm->mapped_address != MAP_FAILED);\n    close(fd);\n    shm->modem_info = (modem_shmem_info_t *)((uint8_t *)shm->mapped_address + 0x100);\n    shm->product_info = (product_shm_info_t *)((uint8_t *)shm->mapped_address + 0xC0);\n}\n\nvoid close_dji_radio_shm(dji_shm_state_t *shm) {\n    munmap(shm->mapped_address, RTOS_SHM_SIZE);\n    shm->mapped_address = NULL;\n    shm->modem_info = NULL;\n    shm->product_info = NULL;\n}\n\nuint16_t dji_radio_latency_ms(dji_shm_state_t *shm) {\n    return shm->product_info->frame_delay_e2e;\n}\n\nuint16_t dji_radio_mbits(dji_shm_state_t *shm) {\n    return shm->modem_info->channel_status;\n}"
  },
  {
    "path": "jni/hw/dji_radio_shm.h",
    "content": "\n#pragma once\n#include <stdint.h>\n\n#define RTOS_SHM_ADDRESS 0xfffc1000\n#define RTOS_SHM_SIZE 0x1000\n\n#define RTOS_PRODUCT_OFFSET 0xc0\n#define RTOS_MODEM_OFFSET 0x100\n\ntypedef struct modem_shmem_info_s {\n    uint32_t frm_idx;\n    uint16_t frm_isI;\n    uint16_t frm_len;\n    uint16_t frm_dsti;\n    uint16_t frm_dstf;\n    uint16_t channel_status;\n    uint16_t dec_err_status;\n    uint16_t cur_time;\n    uint16_t delta_time;\n    uint16_t dbg_msc;\n    uint8_t dbg_ap_ready;\n    uint8_t dbg_cp_ready;\n    uint32_t local_id;\n    uint8_t cp_state;\n    uint8_t cp_report;\n    uint8_t cp_report_seq;\n    uint8_t client_type;\n    uint32_t cp_boot_status0;\n    uint32_t cp_boot_status1;\n    uint32_t board_version;\n    uint32_t board_sub_version;\n    uint8_t machine_role;\n    uint8_t is_reverse;\n    int8_t cp_tx_power;\n    uint8_t gnd_type;\n    uint32_t cp_sssfn;\n    uint32_t ulow_en;\n    uint8_t mipi_rx_response;\n    uint8_t liveview_broken_status;\n    uint8_t reserved01;\n    uint8_t reserved02;\n    uint8_t ap_reboot_flag;\n    uint8_t ap_reboot_ack_flag;\n    uint8_t secure_sync_flag;\n    uint8_t reserve00;\n    uint8_t area_state;\n    uint8_t area_substate;\n    uint8_t GsCtrl;\n    uint8_t GsSubState;\n    uint8_t WaterLevel[4];\n    uint16_t RxCntStastic[4];\n    uint16_t TxCntStastic[4];\n    uint8_t Reserved[8];\n    uint32_t com_uart_status;\n    uint32_t fcr_rx_status;\n    uint32_t fcr_tx_status;\n    uint32_t frm_idx_for_display;\n    uint32_t frm_delay_for_display;\n    uint16_t wifi_sdr_mode;\n    uint16_t frm_isI_for_display;\n    uint32_t country_code;\n    uint16_t frm_len_for_display;\n    uint16_t delta_time_for_display;\n    uint8_t uint8_t_reboot_reason;\n    uint8_t field_0x85;\n    uint8_t field_0x86;\n    uint8_t field_0x87;\n    uint64_t cpa7_version;\n    uint64_t dsp_version;\n    uint8_t u8_dual_band_capability;\n} __attribute__((packed)) modem_shmem_info_t;\n\ntypedef struct product_shm_info_s {\n    uint16_t frm_width;\n    uint16_t frm_height;\n    uint8_t fps;\n    uint8_t enc_strategy;\n    uint16_t lcdc_underflow_cnt;\n    uint32_t enc_sto_frm_dropped;\n    uint32_t enc_lv_frm_dropped;\n    uint32_t mipi_csi_frm_dropped;\n    uint32_t display_frm_dropped;\n    uint64_t audio_pts;\n    uint32_t local_fps_num;\n    uint32_t local_fps_den;\n    uint16_t frame_delay_e2e;\n    uint16_t cam_frame_interval;\n    uint16_t outliner_frame_interval;\n    uint16_t outliner_frame_interval_cnt;\n    uint16_t max_frame_delay_e2e;\n    uint16_t min_frame_delay_e2e;\n    uint16_t avg_frame_delay_e2e;\n    uint16_t if_switch;\n    uint8_t if_change_pipe;\n    uint8_t if_pb_pause;\n    uint8_t liveview_pipeline_running;\n    uint8_t avIn_pipeline_running;\n    uint8_t avIn_stream_type;\n    uint8_t pb_flush;\n    uint8_t disp_pannel_need_reset;\n    uint8_t pad;\n} __attribute__((packed)) product_shm_info_t;\n\ntypedef struct dji_shm_state_s {\n    void *mapped_address;\n    product_shm_info_t *product_info;\n    modem_shmem_info_t *modem_info;\n} dji_shm_state_t;\n\nuint16_t dji_radio_latency_ms(dji_shm_state_t *shm);\nuint16_t dji_radio_mbits(dji_shm_state_t *shm);\nvoid close_dji_radio_shm(dji_shm_state_t *shm);\nvoid open_dji_radio_shm(dji_shm_state_t *shm);"
  },
  {
    "path": "jni/hw/dji_services.c",
    "content": "#include <string.h>\n#ifdef __ANDROID_API__\n#include <sys/system_properties.h>\n#endif\n\n#define V2_SERVICE_NAME \"dji.glasses_wm150_service\"\n#define V1_SERVICE_NAME \"dji.glasses_service\"\n#define DEVICE_PROPERTY_NAME \"ro.product.device\"\n#define V2_GOGGLES_DEVICE \"pigeon_wm170_gls\"\n\nvoid dji_stop_goggles(int is_v2) {\n#ifdef __ANDROID_API__\n    __system_property_set(is_v2 ? V2_SERVICE_NAME : V1_SERVICE_NAME, \"0\");\n#endif\n}\n\nvoid dji_start_goggles(int is_v2) {\n#ifdef __ANDROID_API__\n    __system_property_set(is_v2 ? V2_SERVICE_NAME : V1_SERVICE_NAME, \"1\");\n#endif\n}\n\nint dji_goggles_are_v2() {\n#ifdef __ANDROID_API__\n    char goggles_version_response[255];\n    int len = __system_property_get(DEVICE_PROPERTY_NAME, &goggles_version_response);\n    return(strcmp(goggles_version_response, V2_GOGGLES_DEVICE) == 0);\n#else\n    return 0;\n#endif\n}"
  },
  {
    "path": "jni/hw/dji_services.h",
    "content": "#pragma once\n\nvoid dji_stop_goggles(int is_v2);\nvoid dji_start_goggles(int is_v2);\nint dji_goggles_are_v2();"
  },
  {
    "path": "jni/hw/duml_hal.h",
    "content": "#pragma once\n#include <stdint.h>\n\ntypedef int32_t duss_result_t;\n\ntypedef uint8_t duss_hal_state_t;\ntypedef uint8_t duss_hal_class_t;\n\nstruct sem_t {\n    uint32_t count;\n};\n\nstruct duss_osal_mutex_attrib_t {\n    char * name;\n};\n\nstruct duss_osal_mutex_handle_t {\n    struct duss_osal_mutex_attrib_t attrib;\n    struct sem_t sema;\n};\n\ntypedef struct duss_osal_mutex_handle_t duss_osal_mutex_handle_t, *Pduss_osal_mutex_handle_t;\n\n\ntypedef struct duss_hal_obj_dev_t duss_hal_obj_dev_t, *Pduss_hal_obj_dev_t;\n\ntypedef struct duss_hal_obj * duss_hal_obj_handle_t;\n\nstruct duss_hal_obj_dev_t {\n    char * name;\n    duss_hal_state_t obj_state;\n    duss_hal_class_t obj_class;\n    uint16_t obj_index;\n    int32_t obj_refcnt;\n    struct duss_osal_mutex_handle_t * obj_lock;\n    struct duss_osal_mutex_handle_t * app_lock;\n    duss_result_t (* open)(duss_hal_obj_handle_t, void *);\n    duss_result_t (* close)(duss_hal_obj_handle_t);\n    duss_result_t (* set_cfg)(duss_hal_obj_handle_t, void *);\n    duss_result_t (* get_cfg)(duss_hal_obj_handle_t, void *);\n    duss_result_t (* start)(duss_hal_obj_handle_t, void *);\n    duss_result_t (* stop)(duss_hal_obj_handle_t, void *);\n};\n\nstruct duss_hal_obj {\n    struct duss_hal_obj_dev_t dev;\n    void * dev_class;\n    void * dev_priv;\n};\n\nstruct duss_hal_device_desc_t {\n    char * name;\n    duss_result_t (* attach)(char *, duss_hal_obj_handle_t *);\n    duss_result_t (* detach)(duss_hal_obj_handle_t);\n    duss_hal_obj_handle_t obj;\n};\n\ntypedef enum duss_pixel_format {\n    DUSS_PIXFMT_COMPRESSED_END=30002,\n    DUSS_PIXFMT_DRAW_V1=30000,\n    DUSS_PIXFMT_DUAL_PLANE_16BIT=40003,\n    DUSS_PIXFMT_DUAL_PLANE_8BIT=40002,\n    DUSS_PIXFMT_GENERAL_END=40004,\n    DUSS_PIXFMT_JPEG_LS=30001,\n    DUSS_PIXFMT_OPAQUE=0,\n    DUSS_PIXFMT_RAW10_PACKED=10004,\n    DUSS_PIXFMT_RAW12_PACKED=10005,\n    DUSS_PIXFMT_RAW12_UNPACKED=10008,\n    DUSS_PIXFMT_RAW14_PACKED=10006,\n    DUSS_PIXFMT_RAW14_UNPACKED=10009,\n    DUSS_PIXFMT_RAW16_OPAQUE=10001,\n    DUSS_PIXFMT_RAW16_UNPACKED=10010,\n    DUSS_PIXFMT_RAW18_PACKED=10007,\n    DUSS_PIXFMT_RAW18_UNPACKED=10011,\n    DUSS_PIXFMT_RAW24_OPAQUE=10002,\n    DUSS_PIXFMT_RAW24_UNPACKED=10012,\n    DUSS_PIXFMT_RAW32_OPAQUE=10003,\n    DUSS_PIXFMT_RAW32_UNPACKED=10013,\n    DUSS_PIXFMT_RAW8_OPAQUE=10000,\n    DUSS_PIXFMT_RAW_END=10014,\n    DUSS_PIXFMT_RGB101010=20003,\n    DUSS_PIXFMT_RGB565=20001,\n    DUSS_PIXFMT_RGB888=20000,\n    DUSS_PIXFMT_RGBA8888=20002,\n    DUSS_PIXFMT_RGBA8888_GOGGLES_V2=20012,\n    DUSS_PIXFMT_RGB_END=20004,\n    DUSS_PIXFMT_SINGLE_PLANE_16BIT=40001,\n    DUSS_PIXFMT_SINGLE_PLANE_8BIT=40000,\n    DUSS_PIXFMT_YUV10_420_END=1005,\n    DUSS_PIXFMT_YUV10_422_END=1105,\n    DUSS_PIXFMT_YUV10_444_END=1203,\n    DUSS_PIXFMT_YUV10_P444_YUV=1202,\n    DUSS_PIXFMT_YUV10_SP420_YUV=1003,\n    DUSS_PIXFMT_YUV10_SP420_YUV16=1001,\n    DUSS_PIXFMT_YUV10_SP420_YVU=1004,\n    DUSS_PIXFMT_YUV10_SP420_YVU16=1002,\n    DUSS_PIXFMT_YUV10_SP422_YUV=1103,\n    DUSS_PIXFMT_YUV10_SP422_YUV16=1101,\n    DUSS_PIXFMT_YUV10_SP422_YVU=1104,\n    DUSS_PIXFMT_YUV10_Y422_YUYV16=1102,\n    DUSS_PIXFMT_YUV10_Y444_UYVA=1201,\n    DUSS_PIXFMT_YUV8_420_END=4,\n    DUSS_PIXFMT_YUV8_422_END=106,\n    DUSS_PIXFMT_YUV8_444_END=203,\n    DUSS_PIXFMT_YUV8_P420_YUV=1,\n    DUSS_PIXFMT_YUV8_P422_YUV=105,\n    DUSS_PIXFMT_YUV8_P444_YUV=202,\n    DUSS_PIXFMT_YUV8_SP420_YUV=2,\n    DUSS_PIXFMT_YUV8_SP420_YVU=3,\n    DUSS_PIXFMT_YUV8_SP422_YUV=103,\n    DUSS_PIXFMT_YUV8_SP422_YVU=104,\n    DUSS_PIXFMT_YUV8_Y422_UYVY=101,\n    DUSS_PIXFMT_YUV8_Y422_YUYV=102,\n    DUSS_PIXFMT_YUV8_Y444_UYV=201,\n    DUSS_PIXFMT_YUV_END=1204\n} duss_pixel_format;\n\ntypedef enum duss_pixel_format duss_pixel_format_t;\n\nstruct duss_object {\n    int ref_count;\n    struct duss_osal_mutex_handle_t * lock;\n    void (* on_released)(struct duss_object *);\n};\n\ntypedef struct duss_object duss_object_t;\n\ntypedef struct duss_hal_mem_buf * duss_hal_mem_handle_t;\n\ntypedef struct duss_frame_plane duss_frame_plane, *Pduss_frame_plane;\n\ntypedef struct duss_frame_plane duss_frame_plane_t;\n\nstruct duss_frame_plane {\n    int32_t bytes_per_line;\n    int32_t offset;\n    int32_t plane_height;\n    int32_t bytes_written;\n};\n\nstruct __attribute__((__packed__)) duss_frame_buffer {\n    duss_object_t obj;\n    duss_hal_mem_handle_t buffer;\n    duss_pixel_format_t pixel_format;\n    uint32_t unknown;\n    int64_t time_stamp;\n    int32_t width;\n    int32_t height;\n    duss_frame_plane_t planes[4];\n    int32_t plane_count;\n    uint32_t frame_id;\n};\n\ntypedef struct duss_frame_buffer duss_frame_buffer_t;\n\ntypedef struct duss_hal_device_desc_t duss_hal_device_desc_t, *Pduss_hal_device_desc_t;\n\ntypedef struct duss_hal_obj duss_hal_obj, *Pduss_hal_obj;\n\ntypedef struct duss_disp_instance_handle_t duss_disp_instance_handle_t, *Pduss_disp_instance_handle_t;\n\nstruct __attribute__((__packed__)) duss_disp_instance_handle_t {\n    uint8_t is_init;\n    uint8_t field_0x1;\n    uint8_t field_0x2;\n    uint8_t field_0x3;\n    uint32_t current_vop_index;\n    uint32_t ref_cnt;\n    duss_hal_obj_handle_t obj;\n    void * instance_private_data;\n    void * global_disp_info;\n};\n\ntypedef uint8_t duss_disp_vop_id_t;\n\ntypedef uint8_t duss_disp_plane_type_t;\n\ntypedef uint8_t duss_disp_port_id_t;\n\ntypedef struct duss_disp_timing_detail_t duss_disp_timing_detail_t, *Pduss_disp_timing_detail_t;\n\ntypedef enum duss_disp_timing_bitdepth_t {\n    DUSS_DISP_BITDEPTH_10BIT=1,\n    DUSS_DISP_BITDEPTH_12BIT=2,\n    DUSS_DISP_BITDEPTH_8BIT=0\n} duss_disp_timing_bitdepth_t;\n\ntypedef enum duss_disp_timing_fmt_t {\n    DUSS_DISP_FMT_RGB=0,\n    DUSS_DISP_FMT_YUV420=3,\n    DUSS_DISP_FMT_YUV422=2,\n    DUSS_DISP_FMT_YUV444=1\n} duss_disp_timing_fmt_t;\n\nstruct duss_disp_timing_detail_t {\n    int32_t clock;\n    int32_t hdisplay;\n    int32_t hsync_start;\n    int32_t hsync_end;\n    int32_t htotal;\n    int32_t hskew;\n    int32_t vdisplay;\n    int32_t vsync_start;\n    int32_t vsync_end;\n    int32_t vtotal;\n    int32_t vscan;\n    enum duss_disp_timing_fmt_t disp_fmt;\n    enum duss_disp_timing_bitdepth_t disp_bitdepth;\n};\n\ntypedef struct duss_disp_plane_blending_t duss_disp_plane_blending_t, *Pduss_disp_plane_blending_t;\n\ntypedef uint8_t duss_disp_plane_alpha_alg_t;\ntypedef int8_t duss_disp_plane_id_t;\n\nstruct __attribute__((__packed__)) duss_disp_plane_blending_t {\n    uint8_t is_enable;\n    uint8_t glb_alpha_en;\n    uint8_t field_0x2;\n    uint8_t field_0x3;\n    int32_t glb_alpha_val;\n    duss_disp_plane_alpha_alg_t blending_alg;\n    duss_disp_plane_id_t order;\n    uint8_t field_0xa;\n    uint8_t field_0xb;\n    int32_t hoffset;\n    int32_t voffset;\n};\n\ntypedef duss_result_t (frame_pop_handler)(duss_disp_instance_handle_t * , duss_disp_plane_id_t , duss_frame_buffer_t * , void * );\n\nduss_result_t duss_hal_initialize(duss_hal_device_desc_t * );\nduss_result_t duss_hal_deinitialize();\nduss_result_t duss_hal_device_open(char *device_name, void *unknown, duss_hal_obj_handle_t * );\nduss_result_t duss_hal_device_start(duss_hal_obj_handle_t , void * );\nduss_result_t duss_hal_device_close(duss_hal_obj_handle_t );\nduss_result_t duss_hal_device_stop(duss_hal_obj_handle_t );\n\nduss_result_t duss_hal_mem_alloc(duss_hal_obj_handle_t , duss_hal_mem_handle_t * , uint32_t size, uint32_t , uint32_t , uint32_t );\nduss_result_t duss_hal_mem_get_phys_addr(duss_hal_mem_handle_t , void * * );\nduss_result_t duss_hal_mem_map(duss_hal_mem_handle_t , void * * );\nduss_result_t duss_hal_mem_free(duss_hal_mem_handle_t );\nduss_result_t duss_hal_mem_sync(duss_hal_mem_handle_t , uint32_t );\n\nduss_result_t duss_hal_display_open(duss_hal_obj_handle_t , duss_disp_instance_handle_t * * , duss_disp_vop_id_t );\nduss_result_t duss_hal_display_close(duss_hal_obj_handle_t , duss_disp_instance_handle_t * *);\n// sic\nduss_result_t duss_hal_display_aquire_plane(duss_disp_instance_handle_t * , duss_disp_plane_type_t , duss_disp_plane_id_t * );\nduss_result_t duss_hal_display_reset(duss_disp_instance_handle_t *);\nduss_result_t duss_hal_display_register_frame_cycle_callback(duss_disp_instance_handle_t * , duss_disp_plane_id_t , frame_pop_handler * , void * );\nduss_result_t duss_hal_display_timing_detail_get(duss_disp_instance_handle_t * , duss_disp_timing_detail_t * );\nduss_result_t duss_hal_display_port_enable(duss_disp_instance_handle_t * , duss_disp_port_id_t , uint8_t );\nduss_result_t duss_hal_display_plane_blending_set(duss_disp_instance_handle_t * , duss_disp_plane_id_t , duss_disp_plane_blending_t * );\nduss_result_t duss_hal_display_release_plane(duss_disp_instance_handle_t * , duss_disp_plane_id_t );\nduss_result_t duss_hal_display_push_frame(duss_disp_instance_handle_t * , duss_disp_plane_id_t , duss_frame_buffer_t * );\n\nduss_result_t duss_hal_attach_disp(char *param_1,duss_hal_obj **param_2);\nduss_result_t duss_hal_attach_ion_mem(char *param_1,duss_hal_obj **param_2);\nduss_result_t duss_hal_detach_ion_mem();\nduss_result_t duss_hal_detach_disp();\n"
  },
  {
    "path": "jni/json/osd_config.c",
    "content": "#include \"parson.h\"\n#define JSON_CONFIG_PATH \"/opt/etc/package-config/msp-osd/config.json\"\nstatic JSON_Object *root_object = NULL;\nstatic void load_config() {\n   JSON_Value *root_value = NULL;\n    if(root_object == NULL) {\n        root_value = json_parse_file(JSON_CONFIG_PATH);\n        if (json_value_get_type(root_value) != JSONObject) {\n            return;\n        }\n        root_object = json_value_get_object(root_value);\n    }\n}\n\nint get_boolean_config_value(const char* key) {\n    load_config();\n    if (root_object != NULL) {\n        // parson returns -1 for undefined keys, which can happen when\n        // new keys are defined in the schema - ensure they come back as false\n        return json_object_get_boolean(root_object, key) > 0;\n    } else {\n        return 0;\n    }\n}\n\nconst char * get_string_config_value(const char *key)\n{\n    load_config();\n    if (root_object != NULL)\n    {\n        return json_object_get_string(root_object, key);\n    }\n    else\n    {\n        return NULL;\n    }\n}\n\nint get_integer_config_value(const char *key)\n{\n    load_config();\n    if (root_object != NULL)\n    {\n        return (int)json_object_get_number(root_object, key);\n    }\n    else\n    {\n        return 0;\n    }\n}"
  },
  {
    "path": "jni/json/osd_config.h",
    "content": "int get_boolean_config_value(const char* key);\nconst char *get_string_config_value(const char *key);\nint get_integer_config_value(const char *key);"
  },
  {
    "path": "jni/json/parson.c",
    "content": "/*\n SPDX-License-Identifier: MIT\n\n Parson 1.4.0 (https://github.com/kgabis/parson)\n Copyright (c) 2012 - 2022 Krzysztof Gabis\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#ifdef _MSC_VER\n#ifndef _CRT_SECURE_NO_WARNINGS\n#define _CRT_SECURE_NO_WARNINGS\n#endif /* _CRT_SECURE_NO_WARNINGS */\n#endif /* _MSC_VER */\n\n#include \"parson.h\"\n\n#define PARSON_IMPL_VERSION_MAJOR 1\n#define PARSON_IMPL_VERSION_MINOR 4\n#define PARSON_IMPL_VERSION_PATCH 0\n\n#if (PARSON_VERSION_MAJOR != PARSON_IMPL_VERSION_MAJOR)\\\n|| (PARSON_VERSION_MINOR != PARSON_IMPL_VERSION_MINOR)\\\n|| (PARSON_VERSION_PATCH != PARSON_IMPL_VERSION_PATCH)\n#error \"parson version mismatch between parson.c and parson.h\"\n#endif\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <ctype.h>\n#include <math.h>\n#include <errno.h>\n\n/* Apparently sscanf is not implemented in some \"standard\" libraries, so don't use it, if you\n * don't have to. */\n#ifdef sscanf\n#undef sscanf\n#define sscanf THINK_TWICE_ABOUT_USING_SSCANF\n#endif\n\n/* strcpy is unsafe */\n#ifdef strcpy\n#undef strcpy\n#endif\n#define strcpy USE_MEMCPY_INSTEAD_OF_STRCPY\n\n#define STARTING_CAPACITY 16\n#define MAX_NESTING       2048\n\n#ifndef PARSON_DEFAULT_FLOAT_FORMAT\n#define PARSON_DEFAULT_FLOAT_FORMAT \"%1.17g\" /* do not increase precision without incresing NUM_BUF_SIZE */\n#endif\n\n#ifndef PARSON_NUM_BUF_SIZE\n#define PARSON_NUM_BUF_SIZE 64 /* double printed with \"%1.17g\" shouldn't be longer than 25 bytes so let's be paranoid and use 64 */\n#endif\n\n#define SIZEOF_TOKEN(a)       (sizeof(a) - 1)\n#define SKIP_CHAR(str)        ((*str)++)\n#define SKIP_WHITESPACES(str) while (isspace((unsigned char)(**str))) { SKIP_CHAR(str); }\n#define MAX(a, b)             ((a) > (b) ? (a) : (b))\n\n#undef malloc\n#undef free\n\n#if defined(isnan) && defined(isinf)\n#define IS_NUMBER_INVALID(x) (isnan((x)) || isinf((x)))\n#else\n#define IS_NUMBER_INVALID(x) (((x) * 0.0) != 0.0)\n#endif\n\n#define OBJECT_INVALID_IX ((size_t)-1)\n\nstatic JSON_Malloc_Function parson_malloc = malloc;\nstatic JSON_Free_Function parson_free = free;\n\nstatic int parson_escape_slashes = 1;\n\nstatic char *parson_float_format = NULL;\n\n#define IS_CONT(b) (((unsigned char)(b) & 0xC0) == 0x80) /* is utf-8 continuation byte */\n\ntypedef int parson_bool_t;\n\n#define PARSON_TRUE 1\n#define PARSON_FALSE 0\n\ntypedef struct json_string {\n    char *chars;\n    size_t length;\n} JSON_String;\n\n/* Type definitions */\ntypedef union json_value_value {\n    JSON_String  string;\n    double       number;\n    JSON_Object *object;\n    JSON_Array  *array;\n    int          boolean;\n    int          null;\n} JSON_Value_Value;\n\nstruct json_value_t {\n    JSON_Value      *parent;\n    JSON_Value_Type  type;\n    JSON_Value_Value value;\n};\n\nstruct json_object_t {\n    JSON_Value    *wrapping_value;\n    size_t        *cells;\n    unsigned long *hashes;\n    char         **names;\n    JSON_Value   **values;\n    size_t        *cell_ixs;\n    size_t         count;\n    size_t         item_capacity;\n    size_t         cell_capacity;\n};\n\nstruct json_array_t {\n    JSON_Value  *wrapping_value;\n    JSON_Value **items;\n    size_t       count;\n    size_t       capacity;\n};\n\n/* Various */\nstatic char * read_file(const char *filename);\nstatic void   remove_comments(char *string, const char *start_token, const char *end_token);\nstatic char * parson_strndup(const char *string, size_t n);\nstatic char * parson_strdup(const char *string);\nstatic int    hex_char_to_int(char c);\nstatic JSON_Status parse_utf16_hex(const char *string, unsigned int *result);\nstatic int         num_bytes_in_utf8_sequence(unsigned char c);\nstatic JSON_Status   verify_utf8_sequence(const unsigned char *string, int *len);\nstatic parson_bool_t is_valid_utf8(const char *string, size_t string_len);\nstatic parson_bool_t is_decimal(const char *string, size_t length);\nstatic unsigned long hash_string(const char *string, size_t n);\n\n/* JSON Object */\nstatic JSON_Object * json_object_make(JSON_Value *wrapping_value);\nstatic JSON_Status   json_object_init(JSON_Object *object, size_t capacity);\nstatic void          json_object_deinit(JSON_Object *object, parson_bool_t free_keys, parson_bool_t free_values);\nstatic JSON_Status   json_object_grow_and_rehash(JSON_Object *object);\nstatic size_t        json_object_get_cell_ix(const JSON_Object *object, const char *key, size_t key_len, unsigned long hash, parson_bool_t *out_found);\nstatic JSON_Status   json_object_add(JSON_Object *object, char *name, JSON_Value *value);\nstatic JSON_Value  * json_object_getn_value(const JSON_Object *object, const char *name, size_t name_len);\nstatic JSON_Status   json_object_remove_internal(JSON_Object *object, const char *name, parson_bool_t free_value);\nstatic JSON_Status   json_object_dotremove_internal(JSON_Object *object, const char *name, parson_bool_t free_value);\nstatic void          json_object_free(JSON_Object *object);\n\n/* JSON Array */\nstatic JSON_Array * json_array_make(JSON_Value *wrapping_value);\nstatic JSON_Status  json_array_add(JSON_Array *array, JSON_Value *value);\nstatic JSON_Status  json_array_resize(JSON_Array *array, size_t new_capacity);\nstatic void         json_array_free(JSON_Array *array);\n\n/* JSON Value */\nstatic JSON_Value * json_value_init_string_no_copy(char *string, size_t length);\nstatic const JSON_String * json_value_get_string_desc(const JSON_Value *value);\n\n/* Parser */\nstatic JSON_Status   skip_quotes(const char **string);\nstatic JSON_Status   parse_utf16(const char **unprocessed, char **processed);\nstatic char *        process_string(const char *input, size_t input_len, size_t *output_len);\nstatic char *        get_quoted_string(const char **string, size_t *output_string_len);\nstatic JSON_Value *  parse_object_value(const char **string, size_t nesting);\nstatic JSON_Value *  parse_array_value(const char **string, size_t nesting);\nstatic JSON_Value *  parse_string_value(const char **string);\nstatic JSON_Value *  parse_boolean_value(const char **string);\nstatic JSON_Value *  parse_number_value(const char **string);\nstatic JSON_Value *  parse_null_value(const char **string);\nstatic JSON_Value *  parse_value(const char **string, size_t nesting);\n\n/* Serialization */\nstatic int json_serialize_to_buffer_r(const JSON_Value *value, char *buf, int level, parson_bool_t is_pretty, char *num_buf);\nstatic int json_serialize_string(const char *string, size_t len, char *buf);\nstatic int append_indent(char *buf, int level);\nstatic int append_string(char *buf, const char *string);\n\n/* Various */\nstatic char * read_file(const char * filename) {\n    FILE *fp = fopen(filename, \"r\");\n    size_t size_to_read = 0;\n    size_t size_read = 0;\n    long pos;\n    char *file_contents;\n    if (!fp) {\n        return NULL;\n    }\n    fseek(fp, 0L, SEEK_END);\n    pos = ftell(fp);\n    if (pos < 0) {\n        fclose(fp);\n        return NULL;\n    }\n    size_to_read = pos;\n    rewind(fp);\n    file_contents = (char*)parson_malloc(sizeof(char) * (size_to_read + 1));\n    if (!file_contents) {\n        fclose(fp);\n        return NULL;\n    }\n    size_read = fread(file_contents, 1, size_to_read, fp);\n    if (size_read == 0 || ferror(fp)) {\n        fclose(fp);\n        parson_free(file_contents);\n        return NULL;\n    }\n    fclose(fp);\n    file_contents[size_read] = '\\0';\n    return file_contents;\n}\n\nstatic void remove_comments(char *string, const char *start_token, const char *end_token) {\n    parson_bool_t in_string = PARSON_FALSE, escaped = PARSON_FALSE;\n    size_t i;\n    char *ptr = NULL, current_char;\n    size_t start_token_len = strlen(start_token);\n    size_t end_token_len = strlen(end_token);\n    if (start_token_len == 0 || end_token_len == 0) {\n        return;\n    }\n    while ((current_char = *string) != '\\0') {\n        if (current_char == '\\\\' && !escaped) {\n            escaped = PARSON_TRUE;\n            string++;\n            continue;\n        } else if (current_char == '\\\"' && !escaped) {\n            in_string = !in_string;\n        } else if (!in_string && strncmp(string, start_token, start_token_len) == 0) {\n            for(i = 0; i < start_token_len; i++) {\n                string[i] = ' ';\n            }\n            string = string + start_token_len;\n            ptr = strstr(string, end_token);\n            if (!ptr) {\n                return;\n            }\n            for (i = 0; i < (ptr - string) + end_token_len; i++) {\n                string[i] = ' ';\n            }\n            string = ptr + end_token_len - 1;\n        }\n        escaped = PARSON_FALSE;\n        string++;\n    }\n}\n\nstatic char * parson_strndup(const char *string, size_t n) {\n    /* We expect the caller has validated that 'n' fits within the input buffer. */\n    char *output_string = (char*)parson_malloc(n + 1);\n    if (!output_string) {\n        return NULL;\n    }\n    output_string[n] = '\\0';\n    memcpy(output_string, string, n);\n    return output_string;\n}\n\nstatic char * parson_strdup(const char *string) {\n    return parson_strndup(string, strlen(string));\n}\n\nstatic int hex_char_to_int(char c) {\n    if (c >= '0' && c <= '9') {\n        return c - '0';\n    } else if (c >= 'a' && c <= 'f') {\n        return c - 'a' + 10;\n    } else if (c >= 'A' && c <= 'F') {\n        return c - 'A' + 10;\n    }\n    return -1;\n}\n\nstatic JSON_Status parse_utf16_hex(const char *s, unsigned int *result) {\n    int x1, x2, x3, x4;\n    if (s[0] == '\\0' || s[1] == '\\0' || s[2] == '\\0' || s[3] == '\\0') {\n        return JSONFailure;\n    }\n    x1 = hex_char_to_int(s[0]);\n    x2 = hex_char_to_int(s[1]);\n    x3 = hex_char_to_int(s[2]);\n    x4 = hex_char_to_int(s[3]);\n    if (x1 == -1 || x2 == -1 || x3 == -1 || x4 == -1) {\n        return JSONFailure;\n    }\n    *result = (unsigned int)((x1 << 12) | (x2 << 8) | (x3 << 4) | x4);\n    return JSONSuccess;\n}\n\nstatic int num_bytes_in_utf8_sequence(unsigned char c) {\n    if (c == 0xC0 || c == 0xC1 || c > 0xF4 || IS_CONT(c)) {\n        return 0;\n    } else if ((c & 0x80) == 0) {    /* 0xxxxxxx */\n        return 1;\n    } else if ((c & 0xE0) == 0xC0) { /* 110xxxxx */\n        return 2;\n    } else if ((c & 0xF0) == 0xE0) { /* 1110xxxx */\n        return 3;\n    } else if ((c & 0xF8) == 0xF0) { /* 11110xxx */\n        return 4;\n    }\n    return 0; /* won't happen */\n}\n\nstatic JSON_Status verify_utf8_sequence(const unsigned char *string, int *len) {\n    unsigned int cp = 0;\n    *len = num_bytes_in_utf8_sequence(string[0]);\n\n    if (*len == 1) {\n        cp = string[0];\n    } else if (*len == 2 && IS_CONT(string[1])) {\n        cp = string[0] & 0x1F;\n        cp = (cp << 6) | (string[1] & 0x3F);\n    } else if (*len == 3 && IS_CONT(string[1]) && IS_CONT(string[2])) {\n        cp = ((unsigned char)string[0]) & 0xF;\n        cp = (cp << 6) | (string[1] & 0x3F);\n        cp = (cp << 6) | (string[2] & 0x3F);\n    } else if (*len == 4 && IS_CONT(string[1]) && IS_CONT(string[2]) && IS_CONT(string[3])) {\n        cp = string[0] & 0x7;\n        cp = (cp << 6) | (string[1] & 0x3F);\n        cp = (cp << 6) | (string[2] & 0x3F);\n        cp = (cp << 6) | (string[3] & 0x3F);\n    } else {\n        return JSONFailure;\n    }\n\n    /* overlong encodings */\n    if ((cp < 0x80    && *len > 1) ||\n        (cp < 0x800   && *len > 2) ||\n        (cp < 0x10000 && *len > 3)) {\n        return JSONFailure;\n    }\n\n    /* invalid unicode */\n    if (cp > 0x10FFFF) {\n        return JSONFailure;\n    }\n\n    /* surrogate halves */\n    if (cp >= 0xD800 && cp <= 0xDFFF) {\n        return JSONFailure;\n    }\n\n    return JSONSuccess;\n}\n\nstatic int is_valid_utf8(const char *string, size_t string_len) {\n    int len = 0;\n    const char *string_end =  string + string_len;\n    while (string < string_end) {\n        if (verify_utf8_sequence((const unsigned char*)string, &len) != JSONSuccess) {\n            return PARSON_FALSE;\n        }\n        string += len;\n    }\n    return PARSON_TRUE;\n}\n\nstatic parson_bool_t is_decimal(const char *string, size_t length) {\n    if (length > 1 && string[0] == '0' && string[1] != '.') {\n        return PARSON_FALSE;\n    }\n    if (length > 2 && !strncmp(string, \"-0\", 2) && string[2] != '.') {\n        return PARSON_FALSE;\n    }\n    while (length--) {\n        if (strchr(\"xX\", string[length])) {\n            return PARSON_FALSE;\n        }\n    }\n    return PARSON_TRUE;\n}\n\nstatic unsigned long hash_string(const char *string, size_t n) {\n#ifdef PARSON_FORCE_HASH_COLLISIONS\n    (void)string;\n    (void)n;\n    return 0;\n#else\n    unsigned long hash = 5381;\n    unsigned char c;\n    size_t i = 0;\n    for (i = 0; i < n; i++) {\n        c = string[i];\n        if (c == '\\0') {\n            break;\n        }\n        hash = ((hash << 5) + hash) + c; /* hash * 33 + c */\n    }\n    return hash;\n#endif\n}\n\n/* JSON Object */\nstatic JSON_Object * json_object_make(JSON_Value *wrapping_value) {\n    JSON_Status res = JSONFailure;\n    JSON_Object *new_obj = (JSON_Object*)parson_malloc(sizeof(JSON_Object));\n    if (new_obj == NULL) {\n        return NULL;\n    }\n    new_obj->wrapping_value = wrapping_value;\n    res = json_object_init(new_obj, 0);\n    if (res != JSONSuccess) {\n        parson_free(new_obj);\n        return NULL;\n    }\n    return new_obj;\n}\n\nstatic JSON_Status json_object_init(JSON_Object *object, size_t capacity) {\n    unsigned int i = 0;\n\n    object->cells = NULL;\n    object->names = NULL;\n    object->values = NULL;\n    object->cell_ixs = NULL;\n    object->hashes = NULL;\n\n    object->count = 0;\n    object->cell_capacity = capacity;\n    object->item_capacity = (unsigned int)(capacity * 0.7f);\n\n    if (capacity == 0) {\n        return JSONSuccess;\n    }\n\n    object->cells = (size_t*)parson_malloc(object->cell_capacity * sizeof(*object->cells));\n    object->names = (char**)parson_malloc(object->item_capacity * sizeof(*object->names));\n    object->values = (JSON_Value**)parson_malloc(object->item_capacity * sizeof(*object->values));\n    object->cell_ixs = (size_t*)parson_malloc(object->item_capacity * sizeof(*object->cell_ixs));\n    object->hashes = (unsigned long*)parson_malloc(object->item_capacity * sizeof(*object->hashes));\n    if (object->cells == NULL\n        || object->names == NULL\n        || object->values == NULL\n        || object->cell_ixs == NULL\n        || object->hashes == NULL) {\n        goto error;\n    }\n    for (i = 0; i < object->cell_capacity; i++) {\n        object->cells[i] = OBJECT_INVALID_IX;\n    }\n    return JSONSuccess;\nerror:\n    parson_free(object->cells);\n    parson_free(object->names);\n    parson_free(object->values);\n    parson_free(object->cell_ixs);\n    parson_free(object->hashes);\n    return JSONFailure;\n}\n\nstatic void json_object_deinit(JSON_Object *object, parson_bool_t free_keys, parson_bool_t free_values) {\n    unsigned int i = 0;\n    for (i = 0; i < object->count; i++) {\n        if (free_keys) {\n            parson_free(object->names[i]);\n        }\n        if (free_values) {\n            json_value_free(object->values[i]);\n        }\n    }\n\n    object->count = 0;\n    object->item_capacity = 0;\n    object->cell_capacity = 0;\n\n    parson_free(object->cells);\n    parson_free(object->names);\n    parson_free(object->values);\n    parson_free(object->cell_ixs);\n    parson_free(object->hashes);\n\n    object->cells = NULL;\n    object->names = NULL;\n    object->values = NULL;\n    object->cell_ixs = NULL;\n    object->hashes = NULL;\n}\n\nstatic JSON_Status json_object_grow_and_rehash(JSON_Object *object) {\n    JSON_Value *wrapping_value = NULL;\n    JSON_Object new_object;\n    char *key = NULL;\n    JSON_Value *value = NULL;\n    unsigned int i = 0;\n    size_t new_capacity = MAX(object->cell_capacity * 2, STARTING_CAPACITY);\n    JSON_Status res = json_object_init(&new_object, new_capacity);\n    if (res != JSONSuccess) {\n        return JSONFailure;\n    }\n\n    wrapping_value = json_object_get_wrapping_value(object);\n    new_object.wrapping_value = wrapping_value;\n\n    for (i = 0; i < object->count; i++) {\n        key = object->names[i];\n        value = object->values[i];\n        res = json_object_add(&new_object, key, value);\n        if (res != JSONSuccess) {\n            json_object_deinit(&new_object, PARSON_FALSE, PARSON_FALSE);\n            return JSONFailure;\n        }\n        value->parent = wrapping_value;\n    }\n    json_object_deinit(object, PARSON_FALSE, PARSON_FALSE);\n    *object = new_object;\n    return JSONSuccess;\n}\n\nstatic size_t json_object_get_cell_ix(const JSON_Object *object, const char *key, size_t key_len, unsigned long hash, parson_bool_t *out_found) {\n    size_t cell_ix = hash & (object->cell_capacity - 1);\n    size_t cell = 0;\n    size_t ix = 0;\n    unsigned int i = 0;\n    unsigned long hash_to_check = 0;\n    const char *key_to_check = NULL;\n    size_t key_to_check_len = 0;\n\n    *out_found = PARSON_FALSE;\n\n    for (i = 0; i < object->cell_capacity; i++) {\n        ix = (cell_ix + i) & (object->cell_capacity - 1);\n        cell = object->cells[ix];\n        if (cell == OBJECT_INVALID_IX) {\n            return ix;\n        }\n        hash_to_check = object->hashes[cell];\n        if (hash != hash_to_check) {\n            continue;\n        }\n        key_to_check = object->names[cell];\n        key_to_check_len = strlen(key_to_check);\n        if (key_to_check_len == key_len && strncmp(key, key_to_check, key_len) == 0) {\n            *out_found = PARSON_TRUE;\n            return ix;\n        }\n    }\n    return OBJECT_INVALID_IX;\n}\n\nstatic JSON_Status json_object_add(JSON_Object *object, char *name, JSON_Value *value) {\n    unsigned long hash = 0;\n    parson_bool_t found = PARSON_FALSE;\n    size_t cell_ix = 0;\n    JSON_Status res = JSONFailure;\n\n    if (!object || !name || !value) {\n        return JSONFailure;\n    }\n\n    hash = hash_string(name, strlen(name));\n    found = PARSON_FALSE;\n    cell_ix = json_object_get_cell_ix(object, name, strlen(name), hash, &found);\n    if (found) {\n        return JSONFailure;\n    }\n\n    if (object->count >= object->item_capacity) {\n        res = json_object_grow_and_rehash(object);\n        if (res != JSONSuccess) {\n            return JSONFailure;\n        }\n        cell_ix = json_object_get_cell_ix(object, name, strlen(name), hash, &found);\n    }\n\n    object->names[object->count] = name;\n    object->cells[cell_ix] = object->count;\n    object->values[object->count] = value;\n    object->cell_ixs[object->count] = cell_ix;\n    object->hashes[object->count] = hash;\n    object->count++;\n    value->parent = json_object_get_wrapping_value(object);\n\n    return JSONSuccess;\n}\n\nstatic JSON_Value * json_object_getn_value(const JSON_Object *object, const char *name, size_t name_len) {\n    unsigned long hash = 0;\n    parson_bool_t found = PARSON_FALSE;\n    size_t cell_ix = 0;\n    size_t item_ix = 0;\n    if (!object || !name) {\n        return NULL;\n    }\n    hash = hash_string(name, name_len);\n    found = PARSON_FALSE;\n    cell_ix = json_object_get_cell_ix(object, name, name_len, hash, &found);\n    if (!found) {\n        return NULL;\n    }\n    item_ix = object->cells[cell_ix];\n    return object->values[item_ix];\n}\n\nstatic JSON_Status json_object_remove_internal(JSON_Object *object, const char *name, parson_bool_t free_value) {\n    unsigned long hash = 0;\n    parson_bool_t found = PARSON_FALSE;\n    size_t cell = 0;\n    size_t item_ix = 0;\n    size_t last_item_ix = 0;\n    size_t i = 0;\n    size_t j = 0;\n    size_t x = 0;\n    size_t k = 0;\n    JSON_Value *val = NULL;\n\n    if (object == NULL) {\n        return JSONFailure;\n    }\n\n    hash = hash_string(name, strlen(name));\n    found = PARSON_FALSE;\n    cell = json_object_get_cell_ix(object, name, strlen(name), hash, &found);\n    if (!found) {\n        return JSONFailure;\n    }\n\n    item_ix = object->cells[cell];\n    if (free_value) {\n        val = object->values[item_ix];\n        json_value_free(val);\n        val = NULL;\n    }\n\n    parson_free(object->names[item_ix]);\n    last_item_ix = object->count - 1;\n    if (item_ix < last_item_ix) {\n        object->names[item_ix] = object->names[last_item_ix];\n        object->values[item_ix] = object->values[last_item_ix];\n        object->cell_ixs[item_ix] = object->cell_ixs[last_item_ix];\n        object->hashes[item_ix] = object->hashes[last_item_ix];\n        object->cells[object->cell_ixs[item_ix]] = item_ix;\n    }\n    object->count--;\n\n    i = cell;\n    j = i;\n    for (x = 0; x < (object->cell_capacity - 1); x++) {\n        j = (j + 1) & (object->cell_capacity - 1);\n        if (object->cells[j] == OBJECT_INVALID_IX) {\n            break;\n        }\n        k = object->hashes[object->cells[j]] & (object->cell_capacity - 1);\n        if ((j > i && (k <= i || k > j))\n         || (j < i && (k <= i && k > j))) {\n            object->cell_ixs[object->cells[j]] = i;\n            object->cells[i] = object->cells[j];\n            i = j;\n        }\n    }\n    object->cells[i] = OBJECT_INVALID_IX;\n    return JSONSuccess;\n}\n\nstatic JSON_Status json_object_dotremove_internal(JSON_Object *object, const char *name, parson_bool_t free_value) {\n    JSON_Value *temp_value = NULL;\n    JSON_Object *temp_object = NULL;\n    const char *dot_pos = strchr(name, '.');\n    if (!dot_pos) {\n        return json_object_remove_internal(object, name, free_value);\n    }\n    temp_value = json_object_getn_value(object, name, dot_pos - name);\n    if (json_value_get_type(temp_value) != JSONObject) {\n        return JSONFailure;\n    }\n    temp_object = json_value_get_object(temp_value);\n    return json_object_dotremove_internal(temp_object, dot_pos + 1, free_value);\n}\n\nstatic void json_object_free(JSON_Object *object) {\n    json_object_deinit(object, PARSON_TRUE, PARSON_TRUE);\n    parson_free(object);\n}\n\n/* JSON Array */\nstatic JSON_Array * json_array_make(JSON_Value *wrapping_value) {\n    JSON_Array *new_array = (JSON_Array*)parson_malloc(sizeof(JSON_Array));\n    if (new_array == NULL) {\n        return NULL;\n    }\n    new_array->wrapping_value = wrapping_value;\n    new_array->items = (JSON_Value**)NULL;\n    new_array->capacity = 0;\n    new_array->count = 0;\n    return new_array;\n}\n\nstatic JSON_Status json_array_add(JSON_Array *array, JSON_Value *value) {\n    if (array->count >= array->capacity) {\n        size_t new_capacity = MAX(array->capacity * 2, STARTING_CAPACITY);\n        if (json_array_resize(array, new_capacity) != JSONSuccess) {\n            return JSONFailure;\n        }\n    }\n    value->parent = json_array_get_wrapping_value(array);\n    array->items[array->count] = value;\n    array->count++;\n    return JSONSuccess;\n}\n\nstatic JSON_Status json_array_resize(JSON_Array *array, size_t new_capacity) {\n    JSON_Value **new_items = NULL;\n    if (new_capacity == 0) {\n        return JSONFailure;\n    }\n    new_items = (JSON_Value**)parson_malloc(new_capacity * sizeof(JSON_Value*));\n    if (new_items == NULL) {\n        return JSONFailure;\n    }\n    if (array->items != NULL && array->count > 0) {\n        memcpy(new_items, array->items, array->count * sizeof(JSON_Value*));\n    }\n    parson_free(array->items);\n    array->items = new_items;\n    array->capacity = new_capacity;\n    return JSONSuccess;\n}\n\nstatic void json_array_free(JSON_Array *array) {\n    size_t i;\n    for (i = 0; i < array->count; i++) {\n        json_value_free(array->items[i]);\n    }\n    parson_free(array->items);\n    parson_free(array);\n}\n\n/* JSON Value */\nstatic JSON_Value * json_value_init_string_no_copy(char *string, size_t length) {\n    JSON_Value *new_value = (JSON_Value*)parson_malloc(sizeof(JSON_Value));\n    if (!new_value) {\n        return NULL;\n    }\n    new_value->parent = NULL;\n    new_value->type = JSONString;\n    new_value->value.string.chars = string;\n    new_value->value.string.length = length;\n    return new_value;\n}\n\n/* Parser */\nstatic JSON_Status skip_quotes(const char **string) {\n    if (**string != '\\\"') {\n        return JSONFailure;\n    }\n    SKIP_CHAR(string);\n    while (**string != '\\\"') {\n        if (**string == '\\0') {\n            return JSONFailure;\n        } else if (**string == '\\\\') {\n            SKIP_CHAR(string);\n            if (**string == '\\0') {\n                return JSONFailure;\n            }\n        }\n        SKIP_CHAR(string);\n    }\n    SKIP_CHAR(string);\n    return JSONSuccess;\n}\n\nstatic JSON_Status parse_utf16(const char **unprocessed, char **processed) {\n    unsigned int cp, lead, trail;\n    char *processed_ptr = *processed;\n    const char *unprocessed_ptr = *unprocessed;\n    JSON_Status status = JSONFailure;\n    unprocessed_ptr++; /* skips u */\n    status = parse_utf16_hex(unprocessed_ptr, &cp);\n    if (status != JSONSuccess) {\n        return JSONFailure;\n    }\n    if (cp < 0x80) {\n        processed_ptr[0] = (char)cp; /* 0xxxxxxx */\n    } else if (cp < 0x800) {\n        processed_ptr[0] = ((cp >> 6) & 0x1F) | 0xC0; /* 110xxxxx */\n        processed_ptr[1] = ((cp)      & 0x3F) | 0x80; /* 10xxxxxx */\n        processed_ptr += 1;\n    } else if (cp < 0xD800 || cp > 0xDFFF) {\n        processed_ptr[0] = ((cp >> 12) & 0x0F) | 0xE0; /* 1110xxxx */\n        processed_ptr[1] = ((cp >> 6)  & 0x3F) | 0x80; /* 10xxxxxx */\n        processed_ptr[2] = ((cp)       & 0x3F) | 0x80; /* 10xxxxxx */\n        processed_ptr += 2;\n    } else if (cp >= 0xD800 && cp <= 0xDBFF) { /* lead surrogate (0xD800..0xDBFF) */\n        lead = cp;\n        unprocessed_ptr += 4; /* should always be within the buffer, otherwise previous sscanf would fail */\n        if (*unprocessed_ptr++ != '\\\\' || *unprocessed_ptr++ != 'u') {\n            return JSONFailure;\n        }\n        status = parse_utf16_hex(unprocessed_ptr, &trail);\n        if (status != JSONSuccess || trail < 0xDC00 || trail > 0xDFFF) { /* valid trail surrogate? (0xDC00..0xDFFF) */\n            return JSONFailure;\n        }\n        cp = ((((lead - 0xD800) & 0x3FF) << 10) | ((trail - 0xDC00) & 0x3FF)) + 0x010000;\n        processed_ptr[0] = (((cp >> 18) & 0x07) | 0xF0); /* 11110xxx */\n        processed_ptr[1] = (((cp >> 12) & 0x3F) | 0x80); /* 10xxxxxx */\n        processed_ptr[2] = (((cp >> 6)  & 0x3F) | 0x80); /* 10xxxxxx */\n        processed_ptr[3] = (((cp)       & 0x3F) | 0x80); /* 10xxxxxx */\n        processed_ptr += 3;\n    } else { /* trail surrogate before lead surrogate */\n        return JSONFailure;\n    }\n    unprocessed_ptr += 3;\n    *processed = processed_ptr;\n    *unprocessed = unprocessed_ptr;\n    return JSONSuccess;\n}\n\n\n/* Copies and processes passed string up to supplied length.\nExample: \"\\u006Corem ipsum\" -> lorem ipsum */\nstatic char* process_string(const char *input, size_t input_len, size_t *output_len) {\n    const char *input_ptr = input;\n    size_t initial_size = (input_len + 1) * sizeof(char);\n    size_t final_size = 0;\n    char *output = NULL, *output_ptr = NULL, *resized_output = NULL;\n    output = (char*)parson_malloc(initial_size);\n    if (output == NULL) {\n        goto error;\n    }\n    output_ptr = output;\n    while ((*input_ptr != '\\0') && (size_t)(input_ptr - input) < input_len) {\n        if (*input_ptr == '\\\\') {\n            input_ptr++;\n            switch (*input_ptr) {\n                case '\\\"': *output_ptr = '\\\"'; break;\n                case '\\\\': *output_ptr = '\\\\'; break;\n                case '/':  *output_ptr = '/';  break;\n                case 'b':  *output_ptr = '\\b'; break;\n                case 'f':  *output_ptr = '\\f'; break;\n                case 'n':  *output_ptr = '\\n'; break;\n                case 'r':  *output_ptr = '\\r'; break;\n                case 't':  *output_ptr = '\\t'; break;\n                case 'u':\n                    if (parse_utf16(&input_ptr, &output_ptr) != JSONSuccess) {\n                        goto error;\n                    }\n                    break;\n                default:\n                    goto error;\n            }\n        } else if ((unsigned char)*input_ptr < 0x20) {\n            goto error; /* 0x00-0x19 are invalid characters for json string (http://www.ietf.org/rfc/rfc4627.txt) */\n        } else {\n            *output_ptr = *input_ptr;\n        }\n        output_ptr++;\n        input_ptr++;\n    }\n    *output_ptr = '\\0';\n    /* resize to new length */\n    final_size = (size_t)(output_ptr-output) + 1;\n    /* todo: don't resize if final_size == initial_size */\n    resized_output = (char*)parson_malloc(final_size);\n    if (resized_output == NULL) {\n        goto error;\n    }\n    memcpy(resized_output, output, final_size);\n    *output_len = final_size - 1;\n    parson_free(output);\n    return resized_output;\nerror:\n    parson_free(output);\n    return NULL;\n}\n\n/* Return processed contents of a string between quotes and\n   skips passed argument to a matching quote. */\nstatic char * get_quoted_string(const char **string, size_t *output_string_len) {\n    const char *string_start = *string;\n    size_t input_string_len = 0;\n    JSON_Status status = skip_quotes(string);\n    if (status != JSONSuccess) {\n        return NULL;\n    }\n    input_string_len = *string - string_start - 2; /* length without quotes */\n    return process_string(string_start + 1, input_string_len, output_string_len);\n}\n\nstatic JSON_Value * parse_value(const char **string, size_t nesting) {\n    if (nesting > MAX_NESTING) {\n        return NULL;\n    }\n    SKIP_WHITESPACES(string);\n    switch (**string) {\n        case '{':\n            return parse_object_value(string, nesting + 1);\n        case '[':\n            return parse_array_value(string, nesting + 1);\n        case '\\\"':\n            return parse_string_value(string);\n        case 'f': case 't':\n            return parse_boolean_value(string);\n        case '-':\n        case '0': case '1': case '2': case '3': case '4':\n        case '5': case '6': case '7': case '8': case '9':\n            return parse_number_value(string);\n        case 'n':\n            return parse_null_value(string);\n        default:\n            return NULL;\n    }\n}\n\nstatic JSON_Value * parse_object_value(const char **string, size_t nesting) {\n    JSON_Status status = JSONFailure;\n    JSON_Value *output_value = NULL, *new_value = NULL;\n    JSON_Object *output_object = NULL;\n    char *new_key = NULL;\n\n    output_value = json_value_init_object();\n    if (output_value == NULL) {\n        return NULL;\n    }\n    if (**string != '{') {\n        json_value_free(output_value);\n        return NULL;\n    }\n    output_object = json_value_get_object(output_value);\n    SKIP_CHAR(string);\n    SKIP_WHITESPACES(string);\n    if (**string == '}') { /* empty object */\n        SKIP_CHAR(string);\n        return output_value;\n    }\n    while (**string != '\\0') {\n        size_t key_len = 0;\n        new_key = get_quoted_string(string, &key_len);\n        /* We do not support key names with embedded \\0 chars */\n        if (!new_key) {\n            json_value_free(output_value);\n            return NULL;\n        }\n        if (key_len != strlen(new_key)) {\n            parson_free(new_key);\n            json_value_free(output_value);\n            return NULL;\n        }\n        SKIP_WHITESPACES(string);\n        if (**string != ':') {\n            parson_free(new_key);\n            json_value_free(output_value);\n            return NULL;\n        }\n        SKIP_CHAR(string);\n        new_value = parse_value(string, nesting);\n        if (new_value == NULL) {\n            parson_free(new_key);\n            json_value_free(output_value);\n            return NULL;\n        }\n        status = json_object_add(output_object, new_key, new_value);\n        if (status != JSONSuccess) {\n            parson_free(new_key);\n            json_value_free(new_value);\n            json_value_free(output_value);\n            return NULL;\n        }\n        SKIP_WHITESPACES(string);\n        if (**string != ',') {\n            break;\n        }\n        SKIP_CHAR(string);\n        SKIP_WHITESPACES(string);\n        if (**string == '}') {\n            break;\n        }\n    }\n    SKIP_WHITESPACES(string);\n    if (**string != '}') {\n        json_value_free(output_value);\n        return NULL;\n    }\n    SKIP_CHAR(string);\n    return output_value;\n}\n\nstatic JSON_Value * parse_array_value(const char **string, size_t nesting) {\n    JSON_Value *output_value = NULL, *new_array_value = NULL;\n    JSON_Array *output_array = NULL;\n    output_value = json_value_init_array();\n    if (output_value == NULL) {\n        return NULL;\n    }\n    if (**string != '[') {\n        json_value_free(output_value);\n        return NULL;\n    }\n    output_array = json_value_get_array(output_value);\n    SKIP_CHAR(string);\n    SKIP_WHITESPACES(string);\n    if (**string == ']') { /* empty array */\n        SKIP_CHAR(string);\n        return output_value;\n    }\n    while (**string != '\\0') {\n        new_array_value = parse_value(string, nesting);\n        if (new_array_value == NULL) {\n            json_value_free(output_value);\n            return NULL;\n        }\n        if (json_array_add(output_array, new_array_value) != JSONSuccess) {\n            json_value_free(new_array_value);\n            json_value_free(output_value);\n            return NULL;\n        }\n        SKIP_WHITESPACES(string);\n        if (**string != ',') {\n            break;\n        }\n        SKIP_CHAR(string);\n        SKIP_WHITESPACES(string);\n        if (**string == ']') {\n            break;\n        }\n    }\n    SKIP_WHITESPACES(string);\n    if (**string != ']' || /* Trim array after parsing is over */\n        json_array_resize(output_array, json_array_get_count(output_array)) != JSONSuccess) {\n            json_value_free(output_value);\n            return NULL;\n    }\n    SKIP_CHAR(string);\n    return output_value;\n}\n\nstatic JSON_Value * parse_string_value(const char **string) {\n    JSON_Value *value = NULL;\n    size_t new_string_len = 0;\n    char *new_string = get_quoted_string(string, &new_string_len);\n    if (new_string == NULL) {\n        return NULL;\n    }\n    value = json_value_init_string_no_copy(new_string, new_string_len);\n    if (value == NULL) {\n        parson_free(new_string);\n        return NULL;\n    }\n    return value;\n}\n\nstatic JSON_Value * parse_boolean_value(const char **string) {\n    size_t true_token_size = SIZEOF_TOKEN(\"true\");\n    size_t false_token_size = SIZEOF_TOKEN(\"false\");\n    if (strncmp(\"true\", *string, true_token_size) == 0) {\n        *string += true_token_size;\n        return json_value_init_boolean(1);\n    } else if (strncmp(\"false\", *string, false_token_size) == 0) {\n        *string += false_token_size;\n        return json_value_init_boolean(0);\n    }\n    return NULL;\n}\n\nstatic JSON_Value * parse_number_value(const char **string) {\n    char *end;\n    double number = 0;\n    errno = 0;\n    number = strtod(*string, &end);\n    if (errno == ERANGE && (number <= -HUGE_VAL || number >= HUGE_VAL)) {\n        return NULL;\n    }\n    if ((errno && errno != ERANGE) || !is_decimal(*string, end - *string)) {\n        return NULL;\n    }\n    *string = end;\n    return json_value_init_number(number);\n}\n\nstatic JSON_Value * parse_null_value(const char **string) {\n    size_t token_size = SIZEOF_TOKEN(\"null\");\n    if (strncmp(\"null\", *string, token_size) == 0) {\n        *string += token_size;\n        return json_value_init_null();\n    }\n    return NULL;\n}\n\n/* Serialization */\n#define APPEND_STRING(str) do { written = append_string(buf, (str));\\\n                                if (written < 0) { return -1; }\\\n                                if (buf != NULL) { buf += written; }\\\n                                written_total += written; } while(0)\n\n#define APPEND_INDENT(level) do { written = append_indent(buf, (level));\\\n                                  if (written < 0) { return -1; }\\\n                                  if (buf != NULL) { buf += written; }\\\n                                  written_total += written; } while(0)\n\nstatic int json_serialize_to_buffer_r(const JSON_Value *value, char *buf, int level, parson_bool_t is_pretty, char *num_buf)\n{\n    const char *key = NULL, *string = NULL;\n    JSON_Value *temp_value = NULL;\n    JSON_Array *array = NULL;\n    JSON_Object *object = NULL;\n    size_t i = 0, count = 0;\n    double num = 0.0;\n    int written = -1, written_total = 0;\n    size_t len = 0;\n\n    switch (json_value_get_type(value)) {\n        case JSONArray:\n            array = json_value_get_array(value);\n            count = json_array_get_count(array);\n            APPEND_STRING(\"[\");\n            if (count > 0 && is_pretty) {\n                APPEND_STRING(\"\\n\");\n            }\n            for (i = 0; i < count; i++) {\n                if (is_pretty) {\n                    APPEND_INDENT(level+1);\n                }\n                temp_value = json_array_get_value(array, i);\n                written = json_serialize_to_buffer_r(temp_value, buf, level+1, is_pretty, num_buf);\n                if (written < 0) {\n                    return -1;\n                }\n                if (buf != NULL) {\n                    buf += written;\n                }\n                written_total += written;\n                if (i < (count - 1)) {\n                    APPEND_STRING(\",\");\n                }\n                if (is_pretty) {\n                    APPEND_STRING(\"\\n\");\n                }\n            }\n            if (count > 0 && is_pretty) {\n                APPEND_INDENT(level);\n            }\n            APPEND_STRING(\"]\");\n            return written_total;\n        case JSONObject:\n            object = json_value_get_object(value);\n            count  = json_object_get_count(object);\n            APPEND_STRING(\"{\");\n            if (count > 0 && is_pretty) {\n                APPEND_STRING(\"\\n\");\n            }\n            for (i = 0; i < count; i++) {\n                key = json_object_get_name(object, i);\n                if (key == NULL) {\n                    return -1;\n                }\n                if (is_pretty) {\n                    APPEND_INDENT(level+1);\n                }\n                /* We do not support key names with embedded \\0 chars */\n                written = json_serialize_string(key, strlen(key), buf);\n                if (written < 0) {\n                    return -1;\n                }\n                if (buf != NULL) {\n                    buf += written;\n                }\n                written_total += written;\n                APPEND_STRING(\":\");\n                if (is_pretty) {\n                    APPEND_STRING(\" \");\n                }\n                temp_value = json_object_get_value_at(object, i);\n                written = json_serialize_to_buffer_r(temp_value, buf, level+1, is_pretty, num_buf);\n                if (written < 0) {\n                    return -1;\n                }\n                if (buf != NULL) {\n                    buf += written;\n                }\n                written_total += written;\n                if (i < (count - 1)) {\n                    APPEND_STRING(\",\");\n                }\n                if (is_pretty) {\n                    APPEND_STRING(\"\\n\");\n                }\n            }\n            if (count > 0 && is_pretty) {\n                APPEND_INDENT(level);\n            }\n            APPEND_STRING(\"}\");\n            return written_total;\n        case JSONString:\n            string = json_value_get_string(value);\n            if (string == NULL) {\n                return -1;\n            }\n            len = json_value_get_string_len(value);\n            written = json_serialize_string(string, len, buf);\n            if (written < 0) {\n                return -1;\n            }\n            if (buf != NULL) {\n                buf += written;\n            }\n            written_total += written;\n            return written_total;\n        case JSONBoolean:\n            if (json_value_get_boolean(value)) {\n                APPEND_STRING(\"true\");\n            } else {\n                APPEND_STRING(\"false\");\n            }\n            return written_total;\n        case JSONNumber:\n            num = json_value_get_number(value);\n            if (buf != NULL) {\n                num_buf = buf;\n            }\n            if (parson_float_format) {\n                written = sprintf(num_buf, parson_float_format, num);\n            } else {\n                written = sprintf(num_buf, PARSON_DEFAULT_FLOAT_FORMAT, num);\n            }\n            if (written < 0) {\n                return -1;\n            }\n            if (buf != NULL) {\n                buf += written;\n            }\n            written_total += written;\n            return written_total;\n        case JSONNull:\n            APPEND_STRING(\"null\");\n            return written_total;\n        case JSONError:\n            return -1;\n        default:\n            return -1;\n    }\n}\n\nstatic int json_serialize_string(const char *string, size_t len, char *buf) {\n    size_t i = 0;\n    char c = '\\0';\n    int written = -1, written_total = 0;\n    APPEND_STRING(\"\\\"\");\n    for (i = 0; i < len; i++) {\n        c = string[i];\n        switch (c) {\n            case '\\\"': APPEND_STRING(\"\\\\\\\"\"); break;\n            case '\\\\': APPEND_STRING(\"\\\\\\\\\"); break;\n            case '\\b': APPEND_STRING(\"\\\\b\"); break;\n            case '\\f': APPEND_STRING(\"\\\\f\"); break;\n            case '\\n': APPEND_STRING(\"\\\\n\"); break;\n            case '\\r': APPEND_STRING(\"\\\\r\"); break;\n            case '\\t': APPEND_STRING(\"\\\\t\"); break;\n            case '\\x00': APPEND_STRING(\"\\\\u0000\"); break;\n            case '\\x01': APPEND_STRING(\"\\\\u0001\"); break;\n            case '\\x02': APPEND_STRING(\"\\\\u0002\"); break;\n            case '\\x03': APPEND_STRING(\"\\\\u0003\"); break;\n            case '\\x04': APPEND_STRING(\"\\\\u0004\"); break;\n            case '\\x05': APPEND_STRING(\"\\\\u0005\"); break;\n            case '\\x06': APPEND_STRING(\"\\\\u0006\"); break;\n            case '\\x07': APPEND_STRING(\"\\\\u0007\"); break;\n            /* '\\x08' duplicate: '\\b' */\n            /* '\\x09' duplicate: '\\t' */\n            /* '\\x0a' duplicate: '\\n' */\n            case '\\x0b': APPEND_STRING(\"\\\\u000b\"); break;\n            /* '\\x0c' duplicate: '\\f' */\n            /* '\\x0d' duplicate: '\\r' */\n            case '\\x0e': APPEND_STRING(\"\\\\u000e\"); break;\n            case '\\x0f': APPEND_STRING(\"\\\\u000f\"); break;\n            case '\\x10': APPEND_STRING(\"\\\\u0010\"); break;\n            case '\\x11': APPEND_STRING(\"\\\\u0011\"); break;\n            case '\\x12': APPEND_STRING(\"\\\\u0012\"); break;\n            case '\\x13': APPEND_STRING(\"\\\\u0013\"); break;\n            case '\\x14': APPEND_STRING(\"\\\\u0014\"); break;\n            case '\\x15': APPEND_STRING(\"\\\\u0015\"); break;\n            case '\\x16': APPEND_STRING(\"\\\\u0016\"); break;\n            case '\\x17': APPEND_STRING(\"\\\\u0017\"); break;\n            case '\\x18': APPEND_STRING(\"\\\\u0018\"); break;\n            case '\\x19': APPEND_STRING(\"\\\\u0019\"); break;\n            case '\\x1a': APPEND_STRING(\"\\\\u001a\"); break;\n            case '\\x1b': APPEND_STRING(\"\\\\u001b\"); break;\n            case '\\x1c': APPEND_STRING(\"\\\\u001c\"); break;\n            case '\\x1d': APPEND_STRING(\"\\\\u001d\"); break;\n            case '\\x1e': APPEND_STRING(\"\\\\u001e\"); break;\n            case '\\x1f': APPEND_STRING(\"\\\\u001f\"); break;\n            case '/':\n                if (parson_escape_slashes) {\n                    APPEND_STRING(\"\\\\/\");  /* to make json embeddable in xml\\/html */\n                } else {\n                    APPEND_STRING(\"/\");\n                }\n                break;\n            default:\n                if (buf != NULL) {\n                    buf[0] = c;\n                    buf += 1;\n                }\n                written_total += 1;\n                break;\n        }\n    }\n    APPEND_STRING(\"\\\"\");\n    return written_total;\n}\n\nstatic int append_indent(char *buf, int level) {\n    int i;\n    int written = -1, written_total = 0;\n    for (i = 0; i < level; i++) {\n        APPEND_STRING(\"    \");\n    }\n    return written_total;\n}\n\nstatic int append_string(char *buf, const char *string) {\n    if (buf == NULL) {\n        return (int)strlen(string);\n    }\n    return sprintf(buf, \"%s\", string);\n}\n\n#undef APPEND_STRING\n#undef APPEND_INDENT\n\n/* Parser API */\nJSON_Value * json_parse_file(const char *filename) {\n    char *file_contents = read_file(filename);\n    JSON_Value *output_value = NULL;\n    if (file_contents == NULL) {\n        return NULL;\n    }\n    output_value = json_parse_string(file_contents);\n    parson_free(file_contents);\n    return output_value;\n}\n\nJSON_Value * json_parse_file_with_comments(const char *filename) {\n    char *file_contents = read_file(filename);\n    JSON_Value *output_value = NULL;\n    if (file_contents == NULL) {\n        return NULL;\n    }\n    output_value = json_parse_string_with_comments(file_contents);\n    parson_free(file_contents);\n    return output_value;\n}\n\nJSON_Value * json_parse_string(const char *string) {\n    if (string == NULL) {\n        return NULL;\n    }\n    if (string[0] == '\\xEF' && string[1] == '\\xBB' && string[2] == '\\xBF') {\n        string = string + 3; /* Support for UTF-8 BOM */\n    }\n    return parse_value((const char**)&string, 0);\n}\n\nJSON_Value * json_parse_string_with_comments(const char *string) {\n    JSON_Value *result = NULL;\n    char *string_mutable_copy = NULL, *string_mutable_copy_ptr = NULL;\n    string_mutable_copy = parson_strdup(string);\n    if (string_mutable_copy == NULL) {\n        return NULL;\n    }\n    remove_comments(string_mutable_copy, \"/*\", \"*/\");\n    remove_comments(string_mutable_copy, \"//\", \"\\n\");\n    string_mutable_copy_ptr = string_mutable_copy;\n    result = parse_value((const char**)&string_mutable_copy_ptr, 0);\n    parson_free(string_mutable_copy);\n    return result;\n}\n\n/* JSON Object API */\n\nJSON_Value * json_object_get_value(const JSON_Object *object, const char *name) {\n    if (object == NULL || name == NULL) {\n        return NULL;\n    }\n    return json_object_getn_value(object, name, strlen(name));\n}\n\nconst char * json_object_get_string(const JSON_Object *object, const char *name) {\n    return json_value_get_string(json_object_get_value(object, name));\n}\n\nsize_t json_object_get_string_len(const JSON_Object *object, const char *name) {\n    return json_value_get_string_len(json_object_get_value(object, name));\n}\n\ndouble json_object_get_number(const JSON_Object *object, const char *name) {\n    return json_value_get_number(json_object_get_value(object, name));\n}\n\nJSON_Object * json_object_get_object(const JSON_Object *object, const char *name) {\n    return json_value_get_object(json_object_get_value(object, name));\n}\n\nJSON_Array * json_object_get_array(const JSON_Object *object, const char *name) {\n    return json_value_get_array(json_object_get_value(object, name));\n}\n\nint json_object_get_boolean(const JSON_Object *object, const char *name) {\n    return json_value_get_boolean(json_object_get_value(object, name));\n}\n\nJSON_Value * json_object_dotget_value(const JSON_Object *object, const char *name) {\n    const char *dot_position = strchr(name, '.');\n    if (!dot_position) {\n        return json_object_get_value(object, name);\n    }\n    object = json_value_get_object(json_object_getn_value(object, name, dot_position - name));\n    return json_object_dotget_value(object, dot_position + 1);\n}\n\nconst char * json_object_dotget_string(const JSON_Object *object, const char *name) {\n    return json_value_get_string(json_object_dotget_value(object, name));\n}\n\nsize_t json_object_dotget_string_len(const JSON_Object *object, const char *name) {\n    return json_value_get_string_len(json_object_dotget_value(object, name));\n}\n\ndouble json_object_dotget_number(const JSON_Object *object, const char *name) {\n    return json_value_get_number(json_object_dotget_value(object, name));\n}\n\nJSON_Object * json_object_dotget_object(const JSON_Object *object, const char *name) {\n    return json_value_get_object(json_object_dotget_value(object, name));\n}\n\nJSON_Array * json_object_dotget_array(const JSON_Object *object, const char *name) {\n    return json_value_get_array(json_object_dotget_value(object, name));\n}\n\nint json_object_dotget_boolean(const JSON_Object *object, const char *name) {\n    return json_value_get_boolean(json_object_dotget_value(object, name));\n}\n\nsize_t json_object_get_count(const JSON_Object *object) {\n    return object ? object->count : 0;\n}\n\nconst char * json_object_get_name(const JSON_Object *object, size_t index) {\n    if (object == NULL || index >= json_object_get_count(object)) {\n        return NULL;\n    }\n    return object->names[index];\n}\n\nJSON_Value * json_object_get_value_at(const JSON_Object *object, size_t index) {\n    if (object == NULL || index >= json_object_get_count(object)) {\n        return NULL;\n    }\n    return object->values[index];\n}\n\nJSON_Value *json_object_get_wrapping_value(const JSON_Object *object) {\n    if (!object) {\n        return NULL;\n    }\n    return object->wrapping_value;\n}\n\nint json_object_has_value (const JSON_Object *object, const char *name) {\n    return json_object_get_value(object, name) != NULL;\n}\n\nint json_object_has_value_of_type(const JSON_Object *object, const char *name, JSON_Value_Type type) {\n    JSON_Value *val = json_object_get_value(object, name);\n    return val != NULL && json_value_get_type(val) == type;\n}\n\nint json_object_dothas_value (const JSON_Object *object, const char *name) {\n    return json_object_dotget_value(object, name) != NULL;\n}\n\nint json_object_dothas_value_of_type(const JSON_Object *object, const char *name, JSON_Value_Type type) {\n    JSON_Value *val = json_object_dotget_value(object, name);\n    return val != NULL && json_value_get_type(val) == type;\n}\n\n/* JSON Array API */\nJSON_Value * json_array_get_value(const JSON_Array *array, size_t index) {\n    if (array == NULL || index >= json_array_get_count(array)) {\n        return NULL;\n    }\n    return array->items[index];\n}\n\nconst char * json_array_get_string(const JSON_Array *array, size_t index) {\n    return json_value_get_string(json_array_get_value(array, index));\n}\n\nsize_t json_array_get_string_len(const JSON_Array *array, size_t index) {\n    return json_value_get_string_len(json_array_get_value(array, index));\n}\n\ndouble json_array_get_number(const JSON_Array *array, size_t index) {\n    return json_value_get_number(json_array_get_value(array, index));\n}\n\nJSON_Object * json_array_get_object(const JSON_Array *array, size_t index) {\n    return json_value_get_object(json_array_get_value(array, index));\n}\n\nJSON_Array * json_array_get_array(const JSON_Array *array, size_t index) {\n    return json_value_get_array(json_array_get_value(array, index));\n}\n\nint json_array_get_boolean(const JSON_Array *array, size_t index) {\n    return json_value_get_boolean(json_array_get_value(array, index));\n}\n\nsize_t json_array_get_count(const JSON_Array *array) {\n    return array ? array->count : 0;\n}\n\nJSON_Value * json_array_get_wrapping_value(const JSON_Array *array) {\n    if (!array) {\n        return NULL;\n    }\n    return array->wrapping_value;\n}\n\n/* JSON Value API */\nJSON_Value_Type json_value_get_type(const JSON_Value *value) {\n    return value ? value->type : JSONError;\n}\n\nJSON_Object * json_value_get_object(const JSON_Value *value) {\n    return json_value_get_type(value) == JSONObject ? value->value.object : NULL;\n}\n\nJSON_Array * json_value_get_array(const JSON_Value *value) {\n    return json_value_get_type(value) == JSONArray ? value->value.array : NULL;\n}\n\nstatic const JSON_String * json_value_get_string_desc(const JSON_Value *value) {\n    return json_value_get_type(value) == JSONString ? &value->value.string : NULL;\n}\n\nconst char * json_value_get_string(const JSON_Value *value) {\n    const JSON_String *str = json_value_get_string_desc(value);\n    return str ? str->chars : NULL;\n}\n\nsize_t json_value_get_string_len(const JSON_Value *value) {\n    const JSON_String *str = json_value_get_string_desc(value);\n    return str ? str->length : 0;\n}\n\ndouble json_value_get_number(const JSON_Value *value) {\n    return json_value_get_type(value) == JSONNumber ? value->value.number : 0;\n}\n\nint json_value_get_boolean(const JSON_Value *value) {\n    return json_value_get_type(value) == JSONBoolean ? value->value.boolean : -1;\n}\n\nJSON_Value * json_value_get_parent (const JSON_Value *value) {\n    return value ? value->parent : NULL;\n}\n\nvoid json_value_free(JSON_Value *value) {\n    switch (json_value_get_type(value)) {\n        case JSONObject:\n            json_object_free(value->value.object);\n            break;\n        case JSONString:\n            parson_free(value->value.string.chars);\n            break;\n        case JSONArray:\n            json_array_free(value->value.array);\n            break;\n        default:\n            break;\n    }\n    parson_free(value);\n}\n\nJSON_Value * json_value_init_object(void) {\n    JSON_Value *new_value = (JSON_Value*)parson_malloc(sizeof(JSON_Value));\n    if (!new_value) {\n        return NULL;\n    }\n    new_value->parent = NULL;\n    new_value->type = JSONObject;\n    new_value->value.object = json_object_make(new_value);\n    if (!new_value->value.object) {\n        parson_free(new_value);\n        return NULL;\n    }\n    return new_value;\n}\n\nJSON_Value * json_value_init_array(void) {\n    JSON_Value *new_value = (JSON_Value*)parson_malloc(sizeof(JSON_Value));\n    if (!new_value) {\n        return NULL;\n    }\n    new_value->parent = NULL;\n    new_value->type = JSONArray;\n    new_value->value.array = json_array_make(new_value);\n    if (!new_value->value.array) {\n        parson_free(new_value);\n        return NULL;\n    }\n    return new_value;\n}\n\nJSON_Value * json_value_init_string(const char *string) {\n    if (string == NULL) {\n        return NULL;\n    }\n    return json_value_init_string_with_len(string, strlen(string));\n}\n\nJSON_Value * json_value_init_string_with_len(const char *string, size_t length) {\n    char *copy = NULL;\n    JSON_Value *value;\n    if (string == NULL) {\n        return NULL;\n    }\n    if (!is_valid_utf8(string, length)) {\n        return NULL;\n    }\n    copy = parson_strndup(string, length);\n    if (copy == NULL) {\n        return NULL;\n    }\n    value = json_value_init_string_no_copy(copy, length);\n    if (value == NULL) {\n        parson_free(copy);\n    }\n    return value;\n}\n\nJSON_Value * json_value_init_number(double number) {\n    JSON_Value *new_value = NULL;\n    if (IS_NUMBER_INVALID(number)) {\n        return NULL;\n    }\n    new_value = (JSON_Value*)parson_malloc(sizeof(JSON_Value));\n    if (new_value == NULL) {\n        return NULL;\n    }\n    new_value->parent = NULL;\n    new_value->type = JSONNumber;\n    new_value->value.number = number;\n    return new_value;\n}\n\nJSON_Value * json_value_init_boolean(int boolean) {\n    JSON_Value *new_value = (JSON_Value*)parson_malloc(sizeof(JSON_Value));\n    if (!new_value) {\n        return NULL;\n    }\n    new_value->parent = NULL;\n    new_value->type = JSONBoolean;\n    new_value->value.boolean = boolean ? 1 : 0;\n    return new_value;\n}\n\nJSON_Value * json_value_init_null(void) {\n    JSON_Value *new_value = (JSON_Value*)parson_malloc(sizeof(JSON_Value));\n    if (!new_value) {\n        return NULL;\n    }\n    new_value->parent = NULL;\n    new_value->type = JSONNull;\n    return new_value;\n}\n\nJSON_Value * json_value_deep_copy(const JSON_Value *value) {\n    size_t i = 0;\n    JSON_Value *return_value = NULL, *temp_value_copy = NULL, *temp_value = NULL;\n    const JSON_String *temp_string = NULL;\n    const char *temp_key = NULL;\n    char *temp_string_copy = NULL;\n    JSON_Array *temp_array = NULL, *temp_array_copy = NULL;\n    JSON_Object *temp_object = NULL, *temp_object_copy = NULL;\n    JSON_Status res = JSONFailure;\n    char *key_copy = NULL;\n\n    switch (json_value_get_type(value)) {\n        case JSONArray:\n            temp_array = json_value_get_array(value);\n            return_value = json_value_init_array();\n            if (return_value == NULL) {\n                return NULL;\n            }\n            temp_array_copy = json_value_get_array(return_value);\n            for (i = 0; i < json_array_get_count(temp_array); i++) {\n                temp_value = json_array_get_value(temp_array, i);\n                temp_value_copy = json_value_deep_copy(temp_value);\n                if (temp_value_copy == NULL) {\n                    json_value_free(return_value);\n                    return NULL;\n                }\n                if (json_array_add(temp_array_copy, temp_value_copy) != JSONSuccess) {\n                    json_value_free(return_value);\n                    json_value_free(temp_value_copy);\n                    return NULL;\n                }\n            }\n            return return_value;\n        case JSONObject:\n            temp_object = json_value_get_object(value);\n            return_value = json_value_init_object();\n            if (!return_value) {\n                return NULL;\n            }\n            temp_object_copy = json_value_get_object(return_value);\n            for (i = 0; i < json_object_get_count(temp_object); i++) {\n                temp_key = json_object_get_name(temp_object, i);\n                temp_value = json_object_get_value(temp_object, temp_key);\n                temp_value_copy = json_value_deep_copy(temp_value);\n                if (!temp_value_copy) {\n                    json_value_free(return_value);\n                    return NULL;\n                }\n                key_copy = parson_strdup(temp_key);\n                if (!key_copy) {\n                    json_value_free(temp_value_copy);\n                    json_value_free(return_value);\n                    return NULL;\n                }\n                res = json_object_add(temp_object_copy, key_copy, temp_value_copy);\n                if (res != JSONSuccess) {\n                    parson_free(key_copy);\n                    json_value_free(temp_value_copy);\n                    json_value_free(return_value);\n                    return NULL;\n                }\n            }\n            return return_value;\n        case JSONBoolean:\n            return json_value_init_boolean(json_value_get_boolean(value));\n        case JSONNumber:\n            return json_value_init_number(json_value_get_number(value));\n        case JSONString:\n            temp_string = json_value_get_string_desc(value);\n            if (temp_string == NULL) {\n                return NULL;\n            }\n            temp_string_copy = parson_strndup(temp_string->chars, temp_string->length);\n            if (temp_string_copy == NULL) {\n                return NULL;\n            }\n            return_value = json_value_init_string_no_copy(temp_string_copy, temp_string->length);\n            if (return_value == NULL) {\n                parson_free(temp_string_copy);\n            }\n            return return_value;\n        case JSONNull:\n            return json_value_init_null();\n        case JSONError:\n            return NULL;\n        default:\n            return NULL;\n    }\n}\n\nsize_t json_serialization_size(const JSON_Value *value) {\n    char num_buf[PARSON_NUM_BUF_SIZE]; /* recursively allocating buffer on stack is a bad idea, so let's do it only once */\n    int res = json_serialize_to_buffer_r(value, NULL, 0, PARSON_FALSE, num_buf);\n    return res < 0 ? 0 : (size_t)(res) + 1;\n}\n\nJSON_Status json_serialize_to_buffer(const JSON_Value *value, char *buf, size_t buf_size_in_bytes) {\n    int written = -1;\n    size_t needed_size_in_bytes = json_serialization_size(value);\n    if (needed_size_in_bytes == 0 || buf_size_in_bytes < needed_size_in_bytes) {\n        return JSONFailure;\n    }\n    written = json_serialize_to_buffer_r(value, buf, 0, PARSON_FALSE, NULL);\n    if (written < 0) {\n        return JSONFailure;\n    }\n    return JSONSuccess;\n}\n\nJSON_Status json_serialize_to_file(const JSON_Value *value, const char *filename) {\n    JSON_Status return_code = JSONSuccess;\n    FILE *fp = NULL;\n    char *serialized_string = json_serialize_to_string(value);\n    if (serialized_string == NULL) {\n        return JSONFailure;\n    }\n    fp = fopen(filename, \"w\");\n    if (fp == NULL) {\n        json_free_serialized_string(serialized_string);\n        return JSONFailure;\n    }\n    if (fputs(serialized_string, fp) == EOF) {\n        return_code = JSONFailure;\n    }\n    if (fclose(fp) == EOF) {\n        return_code = JSONFailure;\n    }\n    json_free_serialized_string(serialized_string);\n    return return_code;\n}\n\nchar * json_serialize_to_string(const JSON_Value *value) {\n    JSON_Status serialization_result = JSONFailure;\n    size_t buf_size_bytes = json_serialization_size(value);\n    char *buf = NULL;\n    if (buf_size_bytes == 0) {\n        return NULL;\n    }\n    buf = (char*)parson_malloc(buf_size_bytes);\n    if (buf == NULL) {\n        return NULL;\n    }\n    serialization_result = json_serialize_to_buffer(value, buf, buf_size_bytes);\n    if (serialization_result != JSONSuccess) {\n        json_free_serialized_string(buf);\n        return NULL;\n    }\n    return buf;\n}\n\nsize_t json_serialization_size_pretty(const JSON_Value *value) {\n    char num_buf[PARSON_NUM_BUF_SIZE]; /* recursively allocating buffer on stack is a bad idea, so let's do it only once */\n    int res = json_serialize_to_buffer_r(value, NULL, 0, PARSON_TRUE, num_buf);\n    return res < 0 ? 0 : (size_t)(res) + 1;\n}\n\nJSON_Status json_serialize_to_buffer_pretty(const JSON_Value *value, char *buf, size_t buf_size_in_bytes) {\n    int written = -1;\n    size_t needed_size_in_bytes = json_serialization_size_pretty(value);\n    if (needed_size_in_bytes == 0 || buf_size_in_bytes < needed_size_in_bytes) {\n        return JSONFailure;\n    }\n    written = json_serialize_to_buffer_r(value, buf, 0, PARSON_TRUE, NULL);\n    if (written < 0) {\n        return JSONFailure;\n    }\n    return JSONSuccess;\n}\n\nJSON_Status json_serialize_to_file_pretty(const JSON_Value *value, const char *filename) {\n    JSON_Status return_code = JSONSuccess;\n    FILE *fp = NULL;\n    char *serialized_string = json_serialize_to_string_pretty(value);\n    if (serialized_string == NULL) {\n        return JSONFailure;\n    }\n    fp = fopen(filename, \"w\");\n    if (fp == NULL) {\n        json_free_serialized_string(serialized_string);\n        return JSONFailure;\n    }\n    if (fputs(serialized_string, fp) == EOF) {\n        return_code = JSONFailure;\n    }\n    if (fclose(fp) == EOF) {\n        return_code = JSONFailure;\n    }\n    json_free_serialized_string(serialized_string);\n    return return_code;\n}\n\nchar * json_serialize_to_string_pretty(const JSON_Value *value) {\n    JSON_Status serialization_result = JSONFailure;\n    size_t buf_size_bytes = json_serialization_size_pretty(value);\n    char *buf = NULL;\n    if (buf_size_bytes == 0) {\n        return NULL;\n    }\n    buf = (char*)parson_malloc(buf_size_bytes);\n    if (buf == NULL) {\n        return NULL;\n    }\n    serialization_result = json_serialize_to_buffer_pretty(value, buf, buf_size_bytes);\n    if (serialization_result != JSONSuccess) {\n        json_free_serialized_string(buf);\n        return NULL;\n    }\n    return buf;\n}\n\nvoid json_free_serialized_string(char *string) {\n    parson_free(string);\n}\n\nJSON_Status json_array_remove(JSON_Array *array, size_t ix) {\n    size_t to_move_bytes = 0;\n    if (array == NULL || ix >= json_array_get_count(array)) {\n        return JSONFailure;\n    }\n    json_value_free(json_array_get_value(array, ix));\n    to_move_bytes = (json_array_get_count(array) - 1 - ix) * sizeof(JSON_Value*);\n    memmove(array->items + ix, array->items + ix + 1, to_move_bytes);\n    array->count -= 1;\n    return JSONSuccess;\n}\n\nJSON_Status json_array_replace_value(JSON_Array *array, size_t ix, JSON_Value *value) {\n    if (array == NULL || value == NULL || value->parent != NULL || ix >= json_array_get_count(array)) {\n        return JSONFailure;\n    }\n    json_value_free(json_array_get_value(array, ix));\n    value->parent = json_array_get_wrapping_value(array);\n    array->items[ix] = value;\n    return JSONSuccess;\n}\n\nJSON_Status json_array_replace_string(JSON_Array *array, size_t i, const char* string) {\n    JSON_Value *value = json_value_init_string(string);\n    if (value == NULL) {\n        return JSONFailure;\n    }\n    if (json_array_replace_value(array, i, value) != JSONSuccess) {\n        json_value_free(value);\n        return JSONFailure;\n    }\n    return JSONSuccess;\n}\n\nJSON_Status json_array_replace_string_with_len(JSON_Array *array, size_t i, const char *string, size_t len) {\n    JSON_Value *value = json_value_init_string_with_len(string, len);\n    if (value == NULL) {\n        return JSONFailure;\n    }\n    if (json_array_replace_value(array, i, value) != JSONSuccess) {\n        json_value_free(value);\n        return JSONFailure;\n    }\n    return JSONSuccess;\n}\n\nJSON_Status json_array_replace_number(JSON_Array *array, size_t i, double number) {\n    JSON_Value *value = json_value_init_number(number);\n    if (value == NULL) {\n        return JSONFailure;\n    }\n    if (json_array_replace_value(array, i, value) != JSONSuccess) {\n        json_value_free(value);\n        return JSONFailure;\n    }\n    return JSONSuccess;\n}\n\nJSON_Status json_array_replace_boolean(JSON_Array *array, size_t i, int boolean) {\n    JSON_Value *value = json_value_init_boolean(boolean);\n    if (value == NULL) {\n        return JSONFailure;\n    }\n    if (json_array_replace_value(array, i, value) != JSONSuccess) {\n        json_value_free(value);\n        return JSONFailure;\n    }\n    return JSONSuccess;\n}\n\nJSON_Status json_array_replace_null(JSON_Array *array, size_t i) {\n    JSON_Value *value = json_value_init_null();\n    if (value == NULL) {\n        return JSONFailure;\n    }\n    if (json_array_replace_value(array, i, value) != JSONSuccess) {\n        json_value_free(value);\n        return JSONFailure;\n    }\n    return JSONSuccess;\n}\n\nJSON_Status json_array_clear(JSON_Array *array) {\n    size_t i = 0;\n    if (array == NULL) {\n        return JSONFailure;\n    }\n    for (i = 0; i < json_array_get_count(array); i++) {\n        json_value_free(json_array_get_value(array, i));\n    }\n    array->count = 0;\n    return JSONSuccess;\n}\n\nJSON_Status json_array_append_value(JSON_Array *array, JSON_Value *value) {\n    if (array == NULL || value == NULL || value->parent != NULL) {\n        return JSONFailure;\n    }\n    return json_array_add(array, value);\n}\n\nJSON_Status json_array_append_string(JSON_Array *array, const char *string) {\n    JSON_Value *value = json_value_init_string(string);\n    if (value == NULL) {\n        return JSONFailure;\n    }\n    if (json_array_append_value(array, value) != JSONSuccess) {\n        json_value_free(value);\n        return JSONFailure;\n    }\n    return JSONSuccess;\n}\n\nJSON_Status json_array_append_string_with_len(JSON_Array *array, const char *string, size_t len) {\n    JSON_Value *value = json_value_init_string_with_len(string, len);\n    if (value == NULL) {\n        return JSONFailure;\n    }\n    if (json_array_append_value(array, value) != JSONSuccess) {\n        json_value_free(value);\n        return JSONFailure;\n    }\n    return JSONSuccess;\n}\n\nJSON_Status json_array_append_number(JSON_Array *array, double number) {\n    JSON_Value *value = json_value_init_number(number);\n    if (value == NULL) {\n        return JSONFailure;\n    }\n    if (json_array_append_value(array, value) != JSONSuccess) {\n        json_value_free(value);\n        return JSONFailure;\n    }\n    return JSONSuccess;\n}\n\nJSON_Status json_array_append_boolean(JSON_Array *array, int boolean) {\n    JSON_Value *value = json_value_init_boolean(boolean);\n    if (value == NULL) {\n        return JSONFailure;\n    }\n    if (json_array_append_value(array, value) != JSONSuccess) {\n        json_value_free(value);\n        return JSONFailure;\n    }\n    return JSONSuccess;\n}\n\nJSON_Status json_array_append_null(JSON_Array *array) {\n    JSON_Value *value = json_value_init_null();\n    if (value == NULL) {\n        return JSONFailure;\n    }\n    if (json_array_append_value(array, value) != JSONSuccess) {\n        json_value_free(value);\n        return JSONFailure;\n    }\n    return JSONSuccess;\n}\n\nJSON_Status json_object_set_value(JSON_Object *object, const char *name, JSON_Value *value) {\n    unsigned long hash = 0;\n    parson_bool_t found = PARSON_FALSE;\n    size_t cell_ix = 0;\n    size_t item_ix = 0;\n    JSON_Value *old_value = NULL;\n    char *key_copy = NULL;\n\n    if (!object || !name || !value || value->parent) {\n        return JSONFailure;\n    }\n    hash = hash_string(name, strlen(name));\n    found = PARSON_FALSE;\n    cell_ix = json_object_get_cell_ix(object, name, strlen(name), hash, &found);\n    if (found) {\n        item_ix = object->cells[cell_ix];\n        old_value = object->values[item_ix];\n        json_value_free(old_value);\n        object->values[item_ix] = value;\n        value->parent = json_object_get_wrapping_value(object);\n        return JSONSuccess;\n    }\n    if (object->count >= object->item_capacity) {\n        JSON_Status res = json_object_grow_and_rehash(object);\n        if (res != JSONSuccess) {\n            return JSONFailure;\n        }\n        cell_ix = json_object_get_cell_ix(object, name, strlen(name), hash, &found);\n    }\n    key_copy = parson_strdup(name);\n    if (!key_copy) {\n        return JSONFailure;\n    }\n    object->names[object->count] = key_copy;\n    object->cells[cell_ix] = object->count;\n    object->values[object->count] = value;\n    object->cell_ixs[object->count] = cell_ix;\n    object->hashes[object->count] = hash;\n    object->count++;\n    value->parent = json_object_get_wrapping_value(object);\n    return JSONSuccess;\n}\n\nJSON_Status json_object_set_string(JSON_Object *object, const char *name, const char *string) {\n    JSON_Value *value = json_value_init_string(string);\n    JSON_Status status = json_object_set_value(object, name, value);\n    if (status != JSONSuccess) {\n        json_value_free(value);\n    }\n    return status;\n}\n\nJSON_Status json_object_set_string_with_len(JSON_Object *object, const char *name, const char *string, size_t len) {\n    JSON_Value *value = json_value_init_string_with_len(string, len);\n    JSON_Status status = json_object_set_value(object, name, value);\n    if (status != JSONSuccess) {\n        json_value_free(value);\n    }\n    return status;\n}\n\nJSON_Status json_object_set_number(JSON_Object *object, const char *name, double number) {\n    JSON_Value *value = json_value_init_number(number);\n    JSON_Status status = json_object_set_value(object, name, value);\n    if (status != JSONSuccess) {\n        json_value_free(value);\n    }\n    return status;\n}\n\nJSON_Status json_object_set_boolean(JSON_Object *object, const char *name, int boolean) {\n    JSON_Value *value = json_value_init_boolean(boolean);\n    JSON_Status status = json_object_set_value(object, name, value);\n    if (status != JSONSuccess) {\n        json_value_free(value);\n    }\n    return status;\n}\n\nJSON_Status json_object_set_null(JSON_Object *object, const char *name) {\n    JSON_Value *value = json_value_init_null();\n    JSON_Status status = json_object_set_value(object, name, value);\n    if (status != JSONSuccess) {\n        json_value_free(value);\n    }\n    return status;\n}\n\nJSON_Status json_object_dotset_value(JSON_Object *object, const char *name, JSON_Value *value) {\n    const char *dot_pos = NULL;\n    JSON_Value *temp_value = NULL, *new_value = NULL;\n    JSON_Object *temp_object = NULL, *new_object = NULL;\n    JSON_Status status = JSONFailure;\n    size_t name_len = 0;\n    char *name_copy = NULL;\n    \n    if (object == NULL || name == NULL || value == NULL) {\n        return JSONFailure;\n    }\n    dot_pos = strchr(name, '.');\n    if (dot_pos == NULL) {\n        return json_object_set_value(object, name, value);\n    }\n    name_len = dot_pos - name;\n    temp_value = json_object_getn_value(object, name, name_len);\n    if (temp_value) {\n        /* Don't overwrite existing non-object (unlike json_object_set_value, but it shouldn't be changed at this point) */\n        if (json_value_get_type(temp_value) != JSONObject) {\n            return JSONFailure;\n        }\n        temp_object = json_value_get_object(temp_value);\n        return json_object_dotset_value(temp_object, dot_pos + 1, value);\n    }\n    new_value = json_value_init_object();\n    if (new_value == NULL) {\n        return JSONFailure;\n    }\n    new_object = json_value_get_object(new_value);\n    status = json_object_dotset_value(new_object, dot_pos + 1, value);\n    if (status != JSONSuccess) {\n        json_value_free(new_value);\n        return JSONFailure;\n    }\n    name_copy = parson_strndup(name, name_len);\n    if (!name_copy) {\n        json_object_dotremove_internal(new_object, dot_pos + 1, 0);\n        json_value_free(new_value);\n        return JSONFailure;\n    }\n    status = json_object_add(object, name_copy, new_value);\n    if (status != JSONSuccess) {\n        parson_free(name_copy);\n        json_object_dotremove_internal(new_object, dot_pos + 1, 0);\n        json_value_free(new_value);\n        return JSONFailure;\n    }\n    return JSONSuccess;\n}\n\nJSON_Status json_object_dotset_string(JSON_Object *object, const char *name, const char *string) {\n    JSON_Value *value = json_value_init_string(string);\n    if (value == NULL) {\n        return JSONFailure;\n    }\n    if (json_object_dotset_value(object, name, value) != JSONSuccess) {\n        json_value_free(value);\n        return JSONFailure;\n    }\n    return JSONSuccess;\n}\n\nJSON_Status json_object_dotset_string_with_len(JSON_Object *object, const char *name, const char *string, size_t len) {\n    JSON_Value *value = json_value_init_string_with_len(string, len);\n    if (value == NULL) {\n        return JSONFailure;\n    }\n    if (json_object_dotset_value(object, name, value) != JSONSuccess) {\n        json_value_free(value);\n        return JSONFailure;\n    }\n    return JSONSuccess;\n}\n\nJSON_Status json_object_dotset_number(JSON_Object *object, const char *name, double number) {\n    JSON_Value *value = json_value_init_number(number);\n    if (value == NULL) {\n        return JSONFailure;\n    }\n    if (json_object_dotset_value(object, name, value) != JSONSuccess) {\n        json_value_free(value);\n        return JSONFailure;\n    }\n    return JSONSuccess;\n}\n\nJSON_Status json_object_dotset_boolean(JSON_Object *object, const char *name, int boolean) {\n    JSON_Value *value = json_value_init_boolean(boolean);\n    if (value == NULL) {\n        return JSONFailure;\n    }\n    if (json_object_dotset_value(object, name, value) != JSONSuccess) {\n        json_value_free(value);\n        return JSONFailure;\n    }\n    return JSONSuccess;\n}\n\nJSON_Status json_object_dotset_null(JSON_Object *object, const char *name) {\n    JSON_Value *value = json_value_init_null();\n    if (value == NULL) {\n        return JSONFailure;\n    }\n    if (json_object_dotset_value(object, name, value) != JSONSuccess) {\n        json_value_free(value);\n        return JSONFailure;\n    }\n    return JSONSuccess;\n}\n\nJSON_Status json_object_remove(JSON_Object *object, const char *name) {\n    return json_object_remove_internal(object, name, PARSON_TRUE);\n}\n\nJSON_Status json_object_dotremove(JSON_Object *object, const char *name) {\n    return json_object_dotremove_internal(object, name, PARSON_TRUE);\n}\n\nJSON_Status json_object_clear(JSON_Object *object) {\n    size_t i = 0;\n    if (object == NULL) {\n        return JSONFailure;\n    }\n    for (i = 0; i < json_object_get_count(object); i++) {\n        parson_free(object->names[i]);\n        json_value_free(object->values[i]);\n    }\n    object->count = 0;\n    return JSONSuccess;\n}\n\nJSON_Status json_validate(const JSON_Value *schema, const JSON_Value *value) {\n    JSON_Value *temp_schema_value = NULL, *temp_value = NULL;\n    JSON_Array *schema_array = NULL, *value_array = NULL;\n    JSON_Object *schema_object = NULL, *value_object = NULL;\n    JSON_Value_Type schema_type = JSONError, value_type = JSONError;\n    const char *key = NULL;\n    size_t i = 0, count = 0;\n    if (schema == NULL || value == NULL) {\n        return JSONFailure;\n    }\n    schema_type = json_value_get_type(schema);\n    value_type = json_value_get_type(value);\n    if (schema_type != value_type && schema_type != JSONNull) { /* null represents all values */\n        return JSONFailure;\n    }\n    switch (schema_type) {\n        case JSONArray:\n            schema_array = json_value_get_array(schema);\n            value_array = json_value_get_array(value);\n            count = json_array_get_count(schema_array);\n            if (count == 0) {\n                return JSONSuccess; /* Empty array allows all types */\n            }\n            /* Get first value from array, rest is ignored */\n            temp_schema_value = json_array_get_value(schema_array, 0);\n            for (i = 0; i < json_array_get_count(value_array); i++) {\n                temp_value = json_array_get_value(value_array, i);\n                if (json_validate(temp_schema_value, temp_value) != JSONSuccess) {\n                    return JSONFailure;\n                }\n            }\n            return JSONSuccess;\n        case JSONObject:\n            schema_object = json_value_get_object(schema);\n            value_object = json_value_get_object(value);\n            count = json_object_get_count(schema_object);\n            if (count == 0) {\n                return JSONSuccess; /* Empty object allows all objects */\n            } else if (json_object_get_count(value_object) < count) {\n                return JSONFailure; /* Tested object mustn't have less name-value pairs than schema */\n            }\n            for (i = 0; i < count; i++) {\n                key = json_object_get_name(schema_object, i);\n                temp_schema_value = json_object_get_value(schema_object, key);\n                temp_value = json_object_get_value(value_object, key);\n                if (temp_value == NULL) {\n                    return JSONFailure;\n                }\n                if (json_validate(temp_schema_value, temp_value) != JSONSuccess) {\n                    return JSONFailure;\n                }\n            }\n            return JSONSuccess;\n        case JSONString: case JSONNumber: case JSONBoolean: case JSONNull:\n            return JSONSuccess; /* equality already tested before switch */\n        case JSONError: default:\n            return JSONFailure;\n    }\n}\n\nint json_value_equals(const JSON_Value *a, const JSON_Value *b) {\n    JSON_Object *a_object = NULL, *b_object = NULL;\n    JSON_Array *a_array = NULL, *b_array = NULL;\n    const JSON_String *a_string = NULL, *b_string = NULL;\n    const char *key = NULL;\n    size_t a_count = 0, b_count = 0, i = 0;\n    JSON_Value_Type a_type, b_type;\n    a_type = json_value_get_type(a);\n    b_type = json_value_get_type(b);\n    if (a_type != b_type) {\n        return PARSON_FALSE;\n    }\n    switch (a_type) {\n        case JSONArray:\n            a_array = json_value_get_array(a);\n            b_array = json_value_get_array(b);\n            a_count = json_array_get_count(a_array);\n            b_count = json_array_get_count(b_array);\n            if (a_count != b_count) {\n                return PARSON_FALSE;\n            }\n            for (i = 0; i < a_count; i++) {\n                if (!json_value_equals(json_array_get_value(a_array, i),\n                                       json_array_get_value(b_array, i))) {\n                    return PARSON_FALSE;\n                }\n            }\n            return PARSON_TRUE;\n        case JSONObject:\n            a_object = json_value_get_object(a);\n            b_object = json_value_get_object(b);\n            a_count = json_object_get_count(a_object);\n            b_count = json_object_get_count(b_object);\n            if (a_count != b_count) {\n                return PARSON_FALSE;\n            }\n            for (i = 0; i < a_count; i++) {\n                key = json_object_get_name(a_object, i);\n                if (!json_value_equals(json_object_get_value(a_object, key),\n                                       json_object_get_value(b_object, key))) {\n                    return PARSON_FALSE;\n                }\n            }\n            return PARSON_TRUE;\n        case JSONString:\n            a_string = json_value_get_string_desc(a);\n            b_string = json_value_get_string_desc(b);\n            if (a_string == NULL || b_string == NULL) {\n                return PARSON_FALSE; /* shouldn't happen */\n            }\n            return a_string->length == b_string->length &&\n                   memcmp(a_string->chars, b_string->chars, a_string->length) == 0;\n        case JSONBoolean:\n            return json_value_get_boolean(a) == json_value_get_boolean(b);\n        case JSONNumber:\n            return fabs(json_value_get_number(a) - json_value_get_number(b)) < 0.000001; /* EPSILON */\n        case JSONError:\n            return PARSON_TRUE;\n        case JSONNull:\n            return PARSON_TRUE;\n        default:\n            return PARSON_TRUE;\n    }\n}\n\nJSON_Value_Type json_type(const JSON_Value *value) {\n    return json_value_get_type(value);\n}\n\nJSON_Object * json_object (const JSON_Value *value) {\n    return json_value_get_object(value);\n}\n\nJSON_Array * json_array(const JSON_Value *value) {\n    return json_value_get_array(value);\n}\n\nconst char * json_string(const JSON_Value *value) {\n    return json_value_get_string(value);\n}\n\nsize_t json_string_len(const JSON_Value *value) {\n    return json_value_get_string_len(value);\n}\n\ndouble json_number(const JSON_Value *value) {\n    return json_value_get_number(value);\n}\n\nint json_boolean(const JSON_Value *value) {\n    return json_value_get_boolean(value);\n}\n\nvoid json_set_allocation_functions(JSON_Malloc_Function malloc_fun, JSON_Free_Function free_fun) {\n    parson_malloc = malloc_fun;\n    parson_free = free_fun;\n}\n\nvoid json_set_escape_slashes(int escape_slashes) {\n    parson_escape_slashes = escape_slashes;\n}\n\nvoid json_set_float_serialization_format(const char *format) {\n    if (parson_float_format) {\n        parson_free(parson_float_format);\n    }\n    if (!format) {\n        parson_float_format = NULL;\n        return;\n    }\n    parson_float_format = parson_strdup(format);\n}\n"
  },
  {
    "path": "jni/json/parson.h",
    "content": "/*\n SPDX-License-Identifier: MIT\n\n Parson 1.4.0 (https://github.com/kgabis/parson)\n Copyright (c) 2012 - 2022 Krzysztof Gabis\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\n#ifndef parson_parson_h\n#define parson_parson_h\n\n#ifdef __cplusplus\nextern \"C\"\n{\n#endif\n#if 0\n} /* unconfuse xcode */\n#endif\n\n#define PARSON_VERSION_MAJOR 1\n#define PARSON_VERSION_MINOR 4\n#define PARSON_VERSION_PATCH 0\n\n#define PARSON_VERSION_STRING \"1.4.0\"\n\n#include <stddef.h>   /* size_t */\n\n/* Types and enums */\ntypedef struct json_object_t JSON_Object;\ntypedef struct json_array_t  JSON_Array;\ntypedef struct json_value_t  JSON_Value;\n\nenum json_value_type {\n    JSONError   = -1,\n    JSONNull    = 1,\n    JSONString  = 2,\n    JSONNumber  = 3,\n    JSONObject  = 4,\n    JSONArray   = 5,\n    JSONBoolean = 6\n};\ntypedef int JSON_Value_Type;\n\nenum json_result_t {\n    JSONSuccess = 0,\n    JSONFailure = -1\n};\ntypedef int JSON_Status;\n\ntypedef void * (*JSON_Malloc_Function)(size_t);\ntypedef void   (*JSON_Free_Function)(void *);\n\n/* Call only once, before calling any other function from parson API. If not called, malloc and free\n   from stdlib will be used for all allocations */\nvoid json_set_allocation_functions(JSON_Malloc_Function malloc_fun, JSON_Free_Function free_fun);\n\n/* Sets if slashes should be escaped or not when serializing JSON. By default slashes are escaped.\n This function sets a global setting and is not thread safe. */\nvoid json_set_escape_slashes(int escape_slashes);\n\n/* Sets float format used for serialization of numbers.\n   Make sure it can't serialize to a string longer than PARSON_NUM_BUF_SIZE.\n   If format is null then the default format is used. */\nvoid json_set_float_serialization_format(const char *format);\n\n/* Parses first JSON value in a file, returns NULL in case of error */\nJSON_Value * json_parse_file(const char *filename);\n\n/* Parses first JSON value in a file and ignores comments (/ * * / and //),\n   returns NULL in case of error */\nJSON_Value * json_parse_file_with_comments(const char *filename);\n\n/*  Parses first JSON value in a string, returns NULL in case of error */\nJSON_Value * json_parse_string(const char *string);\n\n/*  Parses first JSON value in a string and ignores comments (/ * * / and //),\n    returns NULL in case of error */\nJSON_Value * json_parse_string_with_comments(const char *string);\n\n/* Serialization */\nsize_t      json_serialization_size(const JSON_Value *value); /* returns 0 on fail */\nJSON_Status json_serialize_to_buffer(const JSON_Value *value, char *buf, size_t buf_size_in_bytes);\nJSON_Status json_serialize_to_file(const JSON_Value *value, const char *filename);\nchar *      json_serialize_to_string(const JSON_Value *value);\n\n/* Pretty serialization */\nsize_t      json_serialization_size_pretty(const JSON_Value *value); /* returns 0 on fail */\nJSON_Status json_serialize_to_buffer_pretty(const JSON_Value *value, char *buf, size_t buf_size_in_bytes);\nJSON_Status json_serialize_to_file_pretty(const JSON_Value *value, const char *filename);\nchar *      json_serialize_to_string_pretty(const JSON_Value *value);\n\nvoid        json_free_serialized_string(char *string); /* frees string from json_serialize_to_string and json_serialize_to_string_pretty */\n\n/* Comparing */\nint  json_value_equals(const JSON_Value *a, const JSON_Value *b);\n\n/* Validation\n   This is *NOT* JSON Schema. It validates json by checking if object have identically\n   named fields with matching types.\n   For example schema {\"name\":\"\", \"age\":0} will validate\n   {\"name\":\"Joe\", \"age\":25} and {\"name\":\"Joe\", \"age\":25, \"gender\":\"m\"},\n   but not {\"name\":\"Joe\"} or {\"name\":\"Joe\", \"age\":\"Cucumber\"}.\n   In case of arrays, only first value in schema is checked against all values in tested array.\n   Empty objects ({}) validate all objects, empty arrays ([]) validate all arrays,\n   null validates values of every type.\n */\nJSON_Status json_validate(const JSON_Value *schema, const JSON_Value *value);\n\n/*\n * JSON Object\n */\nJSON_Value  * json_object_get_value  (const JSON_Object *object, const char *name);\nconst char  * json_object_get_string (const JSON_Object *object, const char *name);\nsize_t        json_object_get_string_len(const JSON_Object *object, const char *name); /* doesn't account for last null character */\nJSON_Object * json_object_get_object (const JSON_Object *object, const char *name);\nJSON_Array  * json_object_get_array  (const JSON_Object *object, const char *name);\ndouble        json_object_get_number (const JSON_Object *object, const char *name); /* returns 0 on fail */\nint           json_object_get_boolean(const JSON_Object *object, const char *name); /* returns -1 on fail */\n\n/* dotget functions enable addressing values with dot notation in nested objects,\n just like in structs or c++/java/c# objects (e.g. objectA.objectB.value).\n Because valid names in JSON can contain dots, some values may be inaccessible\n this way. */\nJSON_Value  * json_object_dotget_value  (const JSON_Object *object, const char *name);\nconst char  * json_object_dotget_string (const JSON_Object *object, const char *name);\nsize_t        json_object_dotget_string_len(const JSON_Object *object, const char *name); /* doesn't account for last null character */\nJSON_Object * json_object_dotget_object (const JSON_Object *object, const char *name);\nJSON_Array  * json_object_dotget_array  (const JSON_Object *object, const char *name);\ndouble        json_object_dotget_number (const JSON_Object *object, const char *name); /* returns 0 on fail */\nint           json_object_dotget_boolean(const JSON_Object *object, const char *name); /* returns -1 on fail */\n\n/* Functions to get available names */\nsize_t        json_object_get_count   (const JSON_Object *object);\nconst char  * json_object_get_name    (const JSON_Object *object, size_t index);\nJSON_Value  * json_object_get_value_at(const JSON_Object *object, size_t index);\nJSON_Value  * json_object_get_wrapping_value(const JSON_Object *object);\n\n/* Functions to check if object has a value with a specific name. Returned value is 1 if object has\n * a value and 0 if it doesn't. dothas functions behave exactly like dotget functions. */\nint json_object_has_value        (const JSON_Object *object, const char *name);\nint json_object_has_value_of_type(const JSON_Object *object, const char *name, JSON_Value_Type type);\n\nint json_object_dothas_value        (const JSON_Object *object, const char *name);\nint json_object_dothas_value_of_type(const JSON_Object *object, const char *name, JSON_Value_Type type);\n\n/* Creates new name-value pair or frees and replaces old value with a new one.\n * json_object_set_value does not copy passed value so it shouldn't be freed afterwards. */\nJSON_Status json_object_set_value(JSON_Object *object, const char *name, JSON_Value *value);\nJSON_Status json_object_set_string(JSON_Object *object, const char *name, const char *string);\nJSON_Status json_object_set_string_with_len(JSON_Object *object, const char *name, const char *string, size_t len);  /* length shouldn't include last null character */\nJSON_Status json_object_set_number(JSON_Object *object, const char *name, double number);\nJSON_Status json_object_set_boolean(JSON_Object *object, const char *name, int boolean);\nJSON_Status json_object_set_null(JSON_Object *object, const char *name);\n\n/* Works like dotget functions, but creates whole hierarchy if necessary.\n * json_object_dotset_value does not copy passed value so it shouldn't be freed afterwards. */\nJSON_Status json_object_dotset_value(JSON_Object *object, const char *name, JSON_Value *value);\nJSON_Status json_object_dotset_string(JSON_Object *object, const char *name, const char *string);\nJSON_Status json_object_dotset_string_with_len(JSON_Object *object, const char *name, const char *string, size_t len); /* length shouldn't include last null character */\nJSON_Status json_object_dotset_number(JSON_Object *object, const char *name, double number);\nJSON_Status json_object_dotset_boolean(JSON_Object *object, const char *name, int boolean);\nJSON_Status json_object_dotset_null(JSON_Object *object, const char *name);\n\n/* Frees and removes name-value pair */\nJSON_Status json_object_remove(JSON_Object *object, const char *name);\n\n/* Works like dotget function, but removes name-value pair only on exact match. */\nJSON_Status json_object_dotremove(JSON_Object *object, const char *key);\n\n/* Removes all name-value pairs in object */\nJSON_Status json_object_clear(JSON_Object *object);\n\n/*\n *JSON Array\n */\nJSON_Value  * json_array_get_value  (const JSON_Array *array, size_t index);\nconst char  * json_array_get_string (const JSON_Array *array, size_t index);\nsize_t        json_array_get_string_len(const JSON_Array *array, size_t index); /* doesn't account for last null character */\nJSON_Object * json_array_get_object (const JSON_Array *array, size_t index);\nJSON_Array  * json_array_get_array  (const JSON_Array *array, size_t index);\ndouble        json_array_get_number (const JSON_Array *array, size_t index); /* returns 0 on fail */\nint           json_array_get_boolean(const JSON_Array *array, size_t index); /* returns -1 on fail */\nsize_t        json_array_get_count  (const JSON_Array *array);\nJSON_Value  * json_array_get_wrapping_value(const JSON_Array *array);\n\n/* Frees and removes value at given index, does nothing and returns JSONFailure if index doesn't exist.\n * Order of values in array may change during execution.  */\nJSON_Status json_array_remove(JSON_Array *array, size_t i);\n\n/* Frees and removes from array value at given index and replaces it with given one.\n * Does nothing and returns JSONFailure if index doesn't exist.\n * json_array_replace_value does not copy passed value so it shouldn't be freed afterwards. */\nJSON_Status json_array_replace_value(JSON_Array *array, size_t i, JSON_Value *value);\nJSON_Status json_array_replace_string(JSON_Array *array, size_t i, const char* string);\nJSON_Status json_array_replace_string_with_len(JSON_Array *array, size_t i, const char *string, size_t len); /* length shouldn't include last null character */\nJSON_Status json_array_replace_number(JSON_Array *array, size_t i, double number);\nJSON_Status json_array_replace_boolean(JSON_Array *array, size_t i, int boolean);\nJSON_Status json_array_replace_null(JSON_Array *array, size_t i);\n\n/* Frees and removes all values from array */\nJSON_Status json_array_clear(JSON_Array *array);\n\n/* Appends new value at the end of array.\n * json_array_append_value does not copy passed value so it shouldn't be freed afterwards. */\nJSON_Status json_array_append_value(JSON_Array *array, JSON_Value *value);\nJSON_Status json_array_append_string(JSON_Array *array, const char *string);\nJSON_Status json_array_append_string_with_len(JSON_Array *array, const char *string, size_t len); /* length shouldn't include last null character */\nJSON_Status json_array_append_number(JSON_Array *array, double number);\nJSON_Status json_array_append_boolean(JSON_Array *array, int boolean);\nJSON_Status json_array_append_null(JSON_Array *array);\n\n/*\n *JSON Value\n */\nJSON_Value * json_value_init_object (void);\nJSON_Value * json_value_init_array  (void);\nJSON_Value * json_value_init_string (const char *string); /* copies passed string */\nJSON_Value * json_value_init_string_with_len(const char *string, size_t length); /* copies passed string, length shouldn't include last null character */\nJSON_Value * json_value_init_number (double number);\nJSON_Value * json_value_init_boolean(int boolean);\nJSON_Value * json_value_init_null   (void);\nJSON_Value * json_value_deep_copy   (const JSON_Value *value);\nvoid         json_value_free        (JSON_Value *value);\n\nJSON_Value_Type json_value_get_type   (const JSON_Value *value);\nJSON_Object *   json_value_get_object (const JSON_Value *value);\nJSON_Array  *   json_value_get_array  (const JSON_Value *value);\nconst char  *   json_value_get_string (const JSON_Value *value);\nsize_t          json_value_get_string_len(const JSON_Value *value); /* doesn't account for last null character */\ndouble          json_value_get_number (const JSON_Value *value);\nint             json_value_get_boolean(const JSON_Value *value);\nJSON_Value  *   json_value_get_parent (const JSON_Value *value);\n\n/* Same as above, but shorter */\nJSON_Value_Type json_type   (const JSON_Value *value);\nJSON_Object *   json_object (const JSON_Value *value);\nJSON_Array  *   json_array  (const JSON_Value *value);\nconst char  *   json_string (const JSON_Value *value);\nsize_t          json_string_len(const JSON_Value *value); /* doesn't account for last null character */\ndouble          json_number (const JSON_Value *value);\nint             json_boolean(const JSON_Value *value);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "jni/libspng/spng.c",
    "content": "/* SPDX-License-Identifier: (BSD-2-Clause AND libpng-2.0) */\n\n#define SPNG__BUILD\n\n#include \"spng.h\"\n\n#include <limits.h>\n#include <string.h>\n#include <stdio.h>\n#include <math.h>\n\n#define ZLIB_CONST\n\n#ifdef __FRAMAC__\n    #define SPNG_DISABLE_OPT\n    #include \"tests/framac_stubs.h\"\n#else\n    #ifdef SPNG_USE_MINIZ\n        #include <miniz.h>\n    #else\n        #include <zlib.h>\n    #endif\n#endif\n\n#ifdef SPNG_MULTITHREADING\n    #include <pthread.h>\n#endif\n\n/* Not build options, edit at your own risk! */\n#define SPNG_READ_SIZE (8192)\n#define SPNG_WRITE_SIZE SPNG_READ_SIZE\n#define SPNG_MAX_CHUNK_COUNT (1000)\n\n#define SPNG_TARGET_CLONES(x)\n\n#ifndef SPNG_DISABLE_OPT\n\n    #if defined(__i386__) || defined(__x86_64__) || defined(_M_IX86) || defined(_M_X64)\n        #define SPNG_X86\n\n        #if defined(__x86_64__) || defined(_M_X64)\n            #define SPNG_X86_64\n        #endif\n\n    #elif defined(__aarch64__) || defined(_M_ARM64) /* || defined(__ARM_NEON) */\n        #define SPNG_ARM /* NOTE: only arm64 builds are tested! */\n    #else\n        #pragma message \"disabling SIMD optimizations for unknown target\"\n        #define SPNG_DISABLE_OPT\n    #endif\n\n    #if defined(SPNG_X86_64) && defined(SPNG_ENABLE_TARGET_CLONES)\n        #undef SPNG_TARGET_CLONES\n        #define SPNG_TARGET_CLONES(x) __attribute__((target_clones(x)))\n    #else\n        #define SPNG_TARGET_CLONES(x)\n    #endif\n\n    #ifndef SPNG_DISABLE_OPT\n        static void defilter_sub3(size_t rowbytes, unsigned char *row);\n        static void defilter_sub4(size_t rowbytes, unsigned char *row);\n        static void defilter_avg3(size_t rowbytes, unsigned char *row, const unsigned char *prev);\n        static void defilter_avg4(size_t rowbytes, unsigned char *row, const unsigned char *prev);\n        static void defilter_paeth3(size_t rowbytes, unsigned char *row, const unsigned char *prev);\n        static void defilter_paeth4(size_t rowbytes, unsigned char *row, const unsigned char *prev);\n\n        #if defined(SPNG_ARM)\n        static uint32_t expand_palette_rgba8_neon(unsigned char *row, const unsigned char *scanline, const unsigned char *plte, uint32_t width);\n        static uint32_t expand_palette_rgb8_neon(unsigned char *row, const unsigned char *scanline, const unsigned char *plte, uint32_t width);\n        #endif\n    #endif\n#endif\n\n#if defined(_MSC_VER)\n    #pragma warning(push)\n    #pragma warning(disable: 4244)\n#endif\n\n#if (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) || defined(__BIG_ENDIAN__)\n    #define SPNG_BIG_ENDIAN\n#else\n    #define SPNG_LITTLE_ENDIAN\n#endif\n\nenum spng_state\n{\n    SPNG_STATE_INVALID = 0,\n    SPNG_STATE_INIT = 1, /* No PNG buffer/stream is set */\n    SPNG_STATE_INPUT, /* Decoder input PNG was set */\n    SPNG_STATE_OUTPUT = SPNG_STATE_INPUT, /* Encoder output was set */\n    SPNG_STATE_IHDR, /* IHDR was read/written */\n    SPNG_STATE_FIRST_IDAT,  /* Encoded up to / reached first IDAT */\n    SPNG_STATE_DECODE_INIT, /* Decoder is ready for progressive reads */\n    SPNG_STATE_ENCODE_INIT = SPNG_STATE_DECODE_INIT,\n    SPNG_STATE_EOI, /* Reached the last scanline/row */\n    SPNG_STATE_LAST_IDAT, /* Reached last IDAT, set at end of decode_image() */\n    SPNG_STATE_AFTER_IDAT, /*  */\n    SPNG_STATE_IEND, /* Reached IEND */\n};\n\nenum spng__internal\n{\n    SPNG__IO_SIGNAL = 1 << 9,\n    SPNG__CTX_FLAGS_ALL = (SPNG_CTX_IGNORE_ADLER32 | SPNG_CTX_ENCODER)\n};\n\n#define SPNG_STR(x) _SPNG_STR(x)\n#define _SPNG_STR(x) #x\n\n#define SPNG_VERSION_STRING SPNG_STR(SPNG_VERSION_MAJOR) \".\" \\\n                            SPNG_STR(SPNG_VERSION_MINOR) \".\" \\\n                            SPNG_STR(SPNG_VERSION_PATCH)\n\n#define SPNG_GET_CHUNK_BOILERPLATE(chunk) \\\n    if(ctx == NULL) return 1; \\\n    int ret = read_chunks(ctx, 0); \\\n    if(ret) return ret; \\\n    if(!ctx->stored.chunk) return SPNG_ECHUNKAVAIL; \\\n    if(chunk == NULL) return 1\n\n#define SPNG_SET_CHUNK_BOILERPLATE(chunk) \\\n    if(ctx == NULL || chunk == NULL) return 1; \\\n    if(ctx->data == NULL && !ctx->encode_only) return SPNG_ENOSRC; \\\n    int ret = read_chunks(ctx, 0); \\\n    if(ret) return ret\n\n/* Determine if the spng_option can be overriden/optimized */\n#define spng__optimize(option) (ctx->optimize_option & (1 << option))\n\nstruct spng_subimage\n{\n    uint32_t width;\n    uint32_t height;\n    size_t out_width; /* byte width based on output format */\n    size_t scanline_width;\n};\n\nstruct spng_text2\n{\n    int type;\n    char *keyword;\n    char *text;\n\n    size_t text_length;\n\n    uint8_t compression_flag; /* iTXt only */\n    char *language_tag; /* iTXt only */\n    char *translated_keyword; /* iTXt only */\n\n    size_t cache_usage;\n    char user_keyword_storage[80];\n};\n\nstruct decode_flags\n{\n    unsigned apply_trns:  1;\n    unsigned apply_gamma: 1;\n    unsigned use_sbit:    1;\n    unsigned indexed:     1;\n    unsigned do_scaling:  1;\n    unsigned interlaced:  1;\n    unsigned same_layout: 1;\n    unsigned zerocopy:    1;\n    unsigned unpack:      1;\n};\n\nstruct encode_flags\n{\n    unsigned interlace:      1;\n    unsigned same_layout:    1;\n    unsigned to_bigendian:   1;\n    unsigned progressive:    1;\n    unsigned finalize:       1;\n\n    enum spng_filter_choice filter_choice;\n};\n\nstruct spng_chunk_bitfield\n{\n    unsigned ihdr: 1;\n    unsigned plte: 1;\n    unsigned chrm: 1;\n    unsigned iccp: 1;\n    unsigned gama: 1;\n    unsigned sbit: 1;\n    unsigned srgb: 1;\n    unsigned text: 1;\n    unsigned bkgd: 1;\n    unsigned hist: 1;\n    unsigned trns: 1;\n    unsigned phys: 1;\n    unsigned splt: 1;\n    unsigned time: 1;\n    unsigned offs: 1;\n    unsigned exif: 1;\n    unsigned unknown: 1;\n};\n\n/* Packed sample iterator */\nstruct spng__iter\n{\n    const uint8_t mask;\n    unsigned shift_amount;\n    const unsigned initial_shift, bit_depth;\n    const unsigned char *samples;\n};\n\nunion spng__decode_plte\n{\n    struct spng_plte_entry rgba[256];\n    unsigned char rgb[256 * 3];\n    unsigned char raw[256 * 4];\n    uint32_t align_this;\n};\n\nstruct spng__zlib_options\n{\n    int compression_level;\n    int window_bits;\n    int mem_level;\n    int strategy;\n    int data_type;\n};\n\ntypedef void spng__undo(spng_ctx *ctx);\n\nstruct spng_ctx\n{\n    size_t data_size;\n    size_t bytes_read;\n    size_t stream_buf_size;\n    unsigned char *stream_buf;\n    const unsigned char *data;\n\n    /* User-defined pointers for streaming */\n    spng_read_fn *read_fn;\n    spng_write_fn *write_fn;\n    void *stream_user_ptr;\n\n    /* Used for buffer reads */\n    const unsigned char *png_base;\n    size_t bytes_left;\n    size_t last_read_size;\n\n    /* Used for encoding */\n    int user_owns_out_png;\n    unsigned char *out_png;\n    unsigned char *write_ptr;\n    size_t out_png_size;\n    size_t bytes_encoded;\n\n    /* These are updated by read/write_header()/read_chunk_bytes() */\n    struct spng_chunk current_chunk;\n    uint32_t cur_chunk_bytes_left;\n    uint32_t cur_actual_crc;\n\n    struct spng_alloc alloc;\n\n    enum spng_ctx_flags flags;\n    enum spng_format fmt;\n\n    enum spng_state state;\n\n    unsigned streaming: 1;\n    unsigned internal_buffer: 1; /* encoding to internal buffer */\n\n    unsigned inflate: 1;\n    unsigned deflate: 1;\n    unsigned encode_only: 1;\n    unsigned strict: 1;\n    unsigned discard: 1;\n    unsigned skip_crc: 1;\n    unsigned keep_unknown: 1;\n    unsigned prev_was_idat: 1;\n\n    struct spng__zlib_options image_options;\n    struct spng__zlib_options text_options;\n\n    spng__undo *undo;\n\n    /* input file contains this chunk */\n    struct spng_chunk_bitfield file;\n\n    /* chunk was stored with spng_set_*() */\n    struct spng_chunk_bitfield user;\n\n    /* chunk was stored by reading or with spng_set_*() */\n    struct spng_chunk_bitfield stored;\n\n    /* used to reset the above in case of an error */\n    struct spng_chunk_bitfield prev_stored;\n\n    struct spng_chunk first_idat, last_idat;\n\n    uint32_t max_width, max_height;\n\n    size_t max_chunk_size;\n    size_t chunk_cache_limit;\n    size_t chunk_cache_usage;\n    uint32_t chunk_count_limit;\n    uint32_t chunk_count_total;\n\n    int crc_action_critical;\n    int crc_action_ancillary;\n\n    uint32_t optimize_option;\n\n    struct spng_ihdr ihdr;\n\n    struct spng_plte plte;\n\n    struct spng_chrm_int chrm_int;\n    struct spng_iccp iccp;\n\n    uint32_t gama;\n\n    struct spng_sbit sbit;\n\n    uint8_t srgb_rendering_intent;\n\n    uint32_t n_text;\n    struct spng_text2 *text_list;\n\n    struct spng_bkgd bkgd;\n    struct spng_hist hist;\n    struct spng_trns trns;\n    struct spng_phys phys;\n\n    uint32_t n_splt;\n    struct spng_splt *splt_list;\n\n    struct spng_time time;\n    struct spng_offs offs;\n    struct spng_exif exif;\n\n    uint32_t n_chunks;\n    struct spng_unknown_chunk *chunk_list;\n\n    struct spng_subimage subimage[7];\n\n    z_stream zstream;\n    unsigned char *scanline_buf, *prev_scanline_buf, *row_buf, *filtered_scanline_buf;\n    unsigned char *scanline, *prev_scanline, *row, *filtered_scanline;\n\n    /* based on fmt */\n    size_t image_size; /* may be zero */\n    size_t image_width;\n\n    unsigned bytes_per_pixel; /* derived from ihdr */\n    unsigned pixel_size; /* derived from spng_format+ihdr */\n    int widest_pass;\n    int last_pass; /* last non-empty pass */\n\n    uint16_t *gamma_lut; /* points to either _lut8 or _lut16 */\n    uint16_t *gamma_lut16;\n    uint16_t gamma_lut8[256];\n    unsigned char trns_px[8];\n    union spng__decode_plte decode_plte;\n    struct spng_sbit decode_sb;\n    struct decode_flags decode_flags;\n    struct spng_row_info row_info;\n\n    struct encode_flags encode_flags;\n};\n\nstatic const uint32_t spng_u32max = INT32_MAX;\n\nstatic const uint32_t adam7_x_start[7] = { 0, 4, 0, 2, 0, 1, 0 };\nstatic const uint32_t adam7_y_start[7] = { 0, 0, 4, 0, 2, 0, 1 };\nstatic const uint32_t adam7_x_delta[7] = { 8, 8, 4, 4, 2, 2, 1 };\nstatic const uint32_t adam7_y_delta[7] = { 8, 8, 8, 4, 4, 2, 2 };\n\nstatic const uint8_t spng_signature[8] = { 137, 80, 78, 71, 13, 10, 26, 10 };\n\nstatic const uint8_t type_ihdr[4] = { 73, 72, 68, 82 };\nstatic const uint8_t type_plte[4] = { 80, 76, 84, 69 };\nstatic const uint8_t type_idat[4] = { 73, 68, 65, 84 };\nstatic const uint8_t type_iend[4] = { 73, 69, 78, 68 };\n\nstatic const uint8_t type_trns[4] = { 116, 82, 78, 83 };\nstatic const uint8_t type_chrm[4] = { 99,  72, 82, 77 };\nstatic const uint8_t type_gama[4] = { 103, 65, 77, 65 };\nstatic const uint8_t type_iccp[4] = { 105, 67, 67, 80 };\nstatic const uint8_t type_sbit[4] = { 115, 66, 73, 84 };\nstatic const uint8_t type_srgb[4] = { 115, 82, 71, 66 };\nstatic const uint8_t type_text[4] = { 116, 69, 88, 116 };\nstatic const uint8_t type_ztxt[4] = { 122, 84, 88, 116 };\nstatic const uint8_t type_itxt[4] = { 105, 84, 88, 116 };\nstatic const uint8_t type_bkgd[4] = { 98,  75, 71, 68 };\nstatic const uint8_t type_hist[4] = { 104, 73, 83, 84 };\nstatic const uint8_t type_phys[4] = { 112, 72, 89, 115 };\nstatic const uint8_t type_splt[4] = { 115, 80, 76, 84 };\nstatic const uint8_t type_time[4] = { 116, 73, 77, 69 };\n\nstatic const uint8_t type_offs[4] = { 111, 70, 70, 115 };\nstatic const uint8_t type_exif[4] = { 101, 88, 73, 102 };\n\nstatic inline void *spng__malloc(spng_ctx *ctx,  size_t size)\n{\n    return ctx->alloc.malloc_fn(size);\n}\n\nstatic inline void *spng__calloc(spng_ctx *ctx, size_t nmemb, size_t size)\n{\n    return ctx->alloc.calloc_fn(nmemb, size);\n}\n\nstatic inline void *spng__realloc(spng_ctx *ctx, void *ptr, size_t size)\n{\n    return ctx->alloc.realloc_fn(ptr, size);\n}\n\nstatic inline void spng__free(spng_ctx *ctx, void *ptr)\n{\n    ctx->alloc.free_fn(ptr);\n}\n\n#if defined(SPNG_USE_MINIZ)\nstatic void *spng__zalloc(void *opaque, size_t items, size_t size)\n#else\nstatic void *spng__zalloc(void *opaque, uInt items, uInt size)\n#endif\n{\n    spng_ctx *ctx = opaque;\n\n    if(size > SIZE_MAX / items) return NULL;\n\n    size_t len = (size_t)items * size;\n\n    return spng__malloc(ctx, len);\n}\n\nstatic void spng__zfree(void *opqaue, void *ptr)\n{\n    spng_ctx *ctx = opqaue;\n    spng__free(ctx, ptr);\n}\n\nstatic inline uint16_t read_u16(const void *src)\n{\n    const unsigned char *data = src;\n\n    return (data[0] & 0xFFU) << 8 | (data[1] & 0xFFU);\n}\n\nstatic inline uint32_t read_u32(const void *src)\n{\n    const unsigned char *data = src;\n\n    return (data[0] & 0xFFUL) << 24 | (data[1] & 0xFFUL) << 16 |\n           (data[2] & 0xFFUL) << 8  | (data[3] & 0xFFUL);\n}\n\nstatic inline int32_t read_s32(const void *src)\n{\n    int32_t ret = (int32_t)read_u32(src);\n\n    return ret;\n}\n\nstatic inline void write_u16(void *dest, uint16_t x)\n{\n    unsigned char *data = dest;\n\n    data[0] = x >> 8;\n    data[1] = x & 0xFF;\n}\n\nstatic inline void write_u32(void *dest, uint32_t x)\n{\n    unsigned char *data = dest;\n\n    data[0] = (x >> 24);\n    data[1] = (x >> 16) & 0xFF;\n    data[2] = (x >> 8) & 0xFF;\n    data[3] = x & 0xFF;\n}\n\nstatic inline void write_s32(void *dest, int32_t x)\n{\n    uint32_t n = x;\n    write_u32(dest, n);\n}\n\n/* Returns an iterator for 1,2,4,8-bit samples */\nstatic struct spng__iter spng__iter_init(unsigned bit_depth, const unsigned char *samples)\n{\n    struct spng__iter iter =\n    {\n        .mask = (uint32_t)(1 << bit_depth) - 1,\n        .shift_amount = 8 - bit_depth,\n        .initial_shift = 8 - bit_depth,\n        .bit_depth = bit_depth,\n        .samples = samples\n    };\n\n    return iter;\n}\n\n/* Returns the current sample unpacked, iterates to the next one */\nstatic inline uint8_t get_sample(struct spng__iter *iter)\n{\n    uint8_t x = (iter->samples[0] >> iter->shift_amount) & iter->mask;\n\n    iter->shift_amount -= iter->bit_depth;\n\n    if(iter->shift_amount > 7)\n    {\n        iter->shift_amount = iter->initial_shift;\n        iter->samples++;\n    }\n\n    return x;\n}\n\nstatic void u16_row_to_host(void *row, size_t size)\n{\n    uint16_t *px = row;\n    size_t i, n = size / 2;\n\n    for(i=0; i < n; i++)\n    {\n        px[i] = read_u16(&px[i]);\n    }\n}\n\nstatic void u16_row_to_bigendian(void *row, size_t size)\n{\n    uint16_t *px = (uint16_t*)row;\n    size_t i, n = size / 2;\n\n    for(i=0; i < n; i++)\n    {\n        write_u16(&px[i], px[i]);\n    }\n}\n\nstatic void rgb8_row_to_rgba8(const unsigned char *row, unsigned char *out, uint32_t n)\n{\n    uint32_t i;\n    for(i=0; i < n; i++)\n    {\n        memcpy(out + i * 4, row + i * 3, 3);\n        out[i*4+3] = 255;\n    }\n}\n\nstatic unsigned num_channels(const struct spng_ihdr *ihdr)\n{\n    switch(ihdr->color_type)\n    {\n        case SPNG_COLOR_TYPE_TRUECOLOR: return 3;\n        case SPNG_COLOR_TYPE_GRAYSCALE_ALPHA: return 2;\n        case SPNG_COLOR_TYPE_TRUECOLOR_ALPHA: return 4;\n        case SPNG_COLOR_TYPE_GRAYSCALE:\n        case SPNG_COLOR_TYPE_INDEXED:\n            return 1;\n        default: return 0;\n    }\n}\n\n/* Calculate scanline width in bits, round up to the nearest byte */\nstatic int calculate_scanline_width(const struct spng_ihdr *ihdr, uint32_t width, size_t *scanline_width)\n{\n    if(ihdr == NULL || !width) return SPNG_EINTERNAL;\n\n    size_t res = num_channels(ihdr) * ihdr->bit_depth;\n\n    if(res > SIZE_MAX / width) return SPNG_EOVERFLOW;\n    res = res * width;\n\n    res += 15; /* Filter byte + 7 for rounding */\n\n    if(res < 15) return SPNG_EOVERFLOW;\n\n    res /= 8;\n\n    if(res > UINT32_MAX) return SPNG_EOVERFLOW;\n\n    *scanline_width = res;\n\n    return 0;\n}\n\nstatic int calculate_subimages(struct spng_ctx *ctx)\n{\n    if(ctx == NULL) return SPNG_EINTERNAL;\n\n    struct spng_ihdr *ihdr = &ctx->ihdr;\n    struct spng_subimage *sub = ctx->subimage;\n\n    if(ihdr->interlace_method == 1)\n    {\n        sub[0].width = (ihdr->width + 7) >> 3;\n        sub[0].height = (ihdr->height + 7) >> 3;\n        sub[1].width = (ihdr->width + 3) >> 3;\n        sub[1].height = (ihdr->height + 7) >> 3;\n        sub[2].width = (ihdr->width + 3) >> 2;\n        sub[2].height = (ihdr->height + 3) >> 3;\n        sub[3].width = (ihdr->width + 1) >> 2;\n        sub[3].height = (ihdr->height + 3) >> 2;\n        sub[4].width = (ihdr->width + 1) >> 1;\n        sub[4].height = (ihdr->height + 1) >> 2;\n        sub[5].width = ihdr->width >> 1;\n        sub[5].height = (ihdr->height + 1) >> 1;\n        sub[6].width = ihdr->width;\n        sub[6].height = ihdr->height >> 1;\n    }\n    else\n    {\n        sub[0].width = ihdr->width;\n        sub[0].height = ihdr->height;\n    }\n\n    int i;\n    for(i=0; i < 7; i++)\n    {\n        if(sub[i].width == 0 || sub[i].height == 0) continue;\n\n        int ret = calculate_scanline_width(ihdr, sub[i].width, &sub[i].scanline_width);\n        if(ret) return ret;\n\n        if(sub[ctx->widest_pass].scanline_width < sub[i].scanline_width) ctx->widest_pass = i;\n\n        ctx->last_pass = i;\n    }\n\n    return 0;\n}\n\nstatic int check_decode_fmt(const struct spng_ihdr *ihdr, const int fmt)\n{\n    switch(fmt)\n    {\n        case SPNG_FMT_RGBA8:\n        case SPNG_FMT_RGBA16:\n        case SPNG_FMT_RGB8:\n        case SPNG_FMT_PNG:\n        case SPNG_FMT_RAW:\n            return 0;\n        case SPNG_FMT_G8:\n        case SPNG_FMT_GA8:\n            if(ihdr->color_type == SPNG_COLOR_TYPE_GRAYSCALE && ihdr->bit_depth <= 8) return 0;\n            else return SPNG_EFMT;\n        case SPNG_FMT_GA16:\n            if(ihdr->color_type == SPNG_COLOR_TYPE_GRAYSCALE && ihdr->bit_depth == 16) return 0;\n            else return SPNG_EFMT;\n        default: return SPNG_EFMT;\n    }\n}\n\nstatic int calculate_image_width(const struct spng_ihdr *ihdr, int fmt, size_t *len)\n{\n    if(ihdr == NULL || len == NULL) return SPNG_EINTERNAL;\n\n    size_t res = ihdr->width;\n    unsigned bytes_per_pixel;\n\n    switch(fmt)\n    {\n        case SPNG_FMT_RGBA8:\n        case SPNG_FMT_GA16:\n            bytes_per_pixel = 4;\n            break;\n        case SPNG_FMT_RGBA16:\n            bytes_per_pixel = 8;\n            break;\n        case SPNG_FMT_RGB8:\n            bytes_per_pixel = 3;\n            break;\n        case SPNG_FMT_PNG:\n        case SPNG_FMT_RAW:\n        {\n            int ret = calculate_scanline_width(ihdr, ihdr->width, &res);\n            if(ret) return ret;\n\n            res -= 1; /* exclude filter byte */\n            bytes_per_pixel = 1;\n            break;\n        }\n        case SPNG_FMT_G8:\n            bytes_per_pixel = 1;\n            break;\n        case SPNG_FMT_GA8:\n            bytes_per_pixel = 2;\n            break;\n        default: return SPNG_EINTERNAL;\n    }\n\n    if(res > SIZE_MAX / bytes_per_pixel) return SPNG_EOVERFLOW;\n    res = res * bytes_per_pixel;\n\n    *len = res;\n\n    return 0;\n}\n\nstatic int calculate_image_size(const struct spng_ihdr *ihdr, int fmt, size_t *len)\n{\n    if(ihdr == NULL || len == NULL) return SPNG_EINTERNAL;\n\n    size_t res = 0;\n\n    int ret = calculate_image_width(ihdr, fmt, &res);\n    if(ret) return ret;\n\n    if(res > SIZE_MAX / ihdr->height) return SPNG_EOVERFLOW;\n    res = res * ihdr->height;\n\n    *len = res;\n\n    return 0;\n}\n\nstatic int increase_cache_usage(spng_ctx *ctx, size_t bytes, int new_chunk)\n{\n    if(ctx == NULL || !bytes) return SPNG_EINTERNAL;\n\n    if(new_chunk)\n    {\n        ctx->chunk_count_total++;\n        if(ctx->chunk_count_total < 1) return SPNG_EOVERFLOW;\n\n        if(ctx->chunk_count_total > ctx->chunk_count_limit) return SPNG_ECHUNK_LIMITS;\n    }\n\n    size_t new_usage = ctx->chunk_cache_usage + bytes;\n\n    if(new_usage < ctx->chunk_cache_usage) return SPNG_EOVERFLOW;\n\n    if(new_usage > ctx->chunk_cache_limit) return SPNG_ECHUNK_LIMITS;\n\n    ctx->chunk_cache_usage = new_usage;\n\n    return 0;\n}\n\nstatic int decrease_cache_usage(spng_ctx *ctx, size_t usage)\n{\n    if(ctx == NULL || !usage) return SPNG_EINTERNAL;\n    if(usage > ctx->chunk_cache_usage) return SPNG_EINTERNAL;\n\n    ctx->chunk_cache_usage -= usage;\n\n    return 0;\n}\n\nstatic int is_critical_chunk(struct spng_chunk *chunk)\n{\n    if(chunk == NULL) return 0;\n    if((chunk->type[0] & (1 << 5)) == 0) return 1;\n\n    return 0;\n}\n\nstatic int decode_err(spng_ctx *ctx, int err)\n{\n    ctx->state = SPNG_STATE_INVALID;\n\n    return err;\n}\n\nstatic int encode_err(spng_ctx *ctx, int err)\n{\n    ctx->state = SPNG_STATE_INVALID;\n\n    return err;\n}\n\nstatic inline int read_data(spng_ctx *ctx, size_t bytes)\n{\n    if(ctx == NULL) return SPNG_EINTERNAL;\n    if(!bytes) return 0;\n\n    if(ctx->streaming && (bytes > SPNG_READ_SIZE)) return SPNG_EINTERNAL;\n\n    int ret = ctx->read_fn(ctx, ctx->stream_user_ptr, ctx->stream_buf, bytes);\n\n    if(ret)\n    {\n        if(ret > 0 || ret < SPNG_IO_ERROR) ret = SPNG_IO_ERROR;\n\n        return ret;\n    }\n\n    ctx->bytes_read += bytes;\n    if(ctx->bytes_read < bytes) return SPNG_EOVERFLOW;\n\n    return 0;\n}\n\n/* Ensure there is enough space for encoding starting at ctx->write_ptr  */\nstatic int require_bytes(spng_ctx *ctx, size_t bytes)\n{\n    if(ctx == NULL) return SPNG_EINTERNAL;\n\n    if(ctx->streaming)\n    {\n        if(bytes > ctx->stream_buf_size)\n        {\n            size_t new_size = ctx->stream_buf_size;\n\n            /* Start at default IDAT size + header + crc */\n            if(new_size < (SPNG_WRITE_SIZE + 12)) new_size = SPNG_WRITE_SIZE + 12;\n\n            if(new_size < bytes) new_size = bytes;\n\n            void *temp = spng__realloc(ctx, ctx->stream_buf, new_size);\n\n            if(temp == NULL) return encode_err(ctx, SPNG_EMEM);\n\n            ctx->stream_buf = temp;\n            ctx->stream_buf_size = bytes;\n            ctx->write_ptr = ctx->stream_buf;\n        }\n\n        return 0;\n    }\n\n    if(!ctx->internal_buffer) return SPNG_ENODST;\n\n    size_t required = ctx->bytes_encoded + bytes;\n    if(required < bytes) return SPNG_EOVERFLOW;\n\n    if(required > ctx->out_png_size)\n    {\n        size_t new_size = ctx->out_png_size;\n\n        /* Start with a size that doesn't require a realloc() 100% of the time */\n        if(new_size < (SPNG_WRITE_SIZE * 2)) new_size = SPNG_WRITE_SIZE * 2;\n\n        /* Prefer the next power of two over the requested size */\n        while(new_size < required)\n        {\n            if(new_size / SIZE_MAX > 2) return encode_err(ctx, SPNG_EOVERFLOW);\n\n            new_size *= 2;\n        }\n\n        void *temp = spng__realloc(ctx, ctx->out_png, new_size);\n\n        if(temp == NULL) return encode_err(ctx, SPNG_EMEM);\n\n        ctx->out_png = temp;\n        ctx->out_png_size = new_size;\n        ctx->write_ptr = ctx->out_png + ctx->bytes_encoded;\n    }\n\n    return 0;\n}\n\nstatic int write_data(spng_ctx *ctx, const void *data, size_t bytes)\n{\n    if(ctx == NULL) return SPNG_EINTERNAL;\n    if(!bytes) return 0;\n\n    if(ctx->streaming)\n    {\n        if(bytes > SPNG_WRITE_SIZE) return SPNG_EINTERNAL;\n\n        int ret = ctx->write_fn(ctx, ctx->stream_user_ptr, (void*)data, bytes);\n\n        if(ret)\n        {\n            if(ret > 0 || ret < SPNG_IO_ERROR) ret = SPNG_IO_ERROR;\n\n            return encode_err(ctx, ret);\n        }\n    }\n    else\n    {\n        int ret = require_bytes(ctx, bytes);\n        if(ret) return encode_err(ctx, ret);\n\n        memcpy(ctx->write_ptr, data, bytes);\n\n        ctx->write_ptr += bytes;\n    }\n\n    ctx->bytes_encoded += bytes;\n    if(ctx->bytes_encoded < bytes) return SPNG_EOVERFLOW;\n\n    return 0;\n}\n\nstatic int write_header(spng_ctx *ctx, const uint8_t chunk_type[4], size_t chunk_length, unsigned char **data)\n{\n    if(ctx == NULL || chunk_type == NULL) return SPNG_EINTERNAL;\n    if(chunk_length > spng_u32max) return SPNG_EINTERNAL;\n\n    size_t total = chunk_length + 12;\n\n    int ret = require_bytes(ctx, total);\n    if(ret) return ret;\n\n    uint32_t crc = crc32(0, NULL, 0);\n    ctx->current_chunk.crc = crc32(crc, chunk_type, 4);\n\n    memcpy(&ctx->current_chunk.type, chunk_type, 4);\n    ctx->current_chunk.length = (uint32_t)chunk_length;\n\n    if(!data) return SPNG_EINTERNAL;\n\n    if(ctx->streaming) *data = ctx->stream_buf + 8;\n    else *data = ctx->write_ptr + 8;\n\n    return 0;\n}\n\nstatic int trim_chunk(spng_ctx *ctx, uint32_t length)\n{\n    if(length > spng_u32max) return SPNG_EINTERNAL;\n    if(length > ctx->current_chunk.length) return SPNG_EINTERNAL;\n\n    ctx->current_chunk.length = length;\n\n    return 0;\n}\n\nstatic int finish_chunk(spng_ctx *ctx)\n{\n    if(ctx == NULL) return SPNG_EINTERNAL;\n\n    struct spng_chunk *chunk = &ctx->current_chunk;\n\n    unsigned char *header;\n    unsigned char *chunk_data;\n\n    if(ctx->streaming)\n    {\n        chunk_data = ctx->stream_buf + 8;\n        header = ctx->stream_buf;\n    }\n    else\n    {\n        chunk_data = ctx->write_ptr + 8;\n        header = ctx->write_ptr;\n    }\n\n    write_u32(header, chunk->length);\n    memcpy(header + 4, chunk->type, 4);\n\n    chunk->crc = crc32(chunk->crc, chunk_data, chunk->length);\n\n    write_u32(chunk_data + chunk->length, chunk->crc);\n\n    if(ctx->streaming)\n    {\n        const unsigned char *ptr = ctx->stream_buf;\n        uint32_t bytes_left = chunk->length + 12;\n        uint32_t len = 0;\n\n        while(bytes_left)\n        {\n            ptr += len;\n            len = SPNG_WRITE_SIZE;\n\n            if(len > bytes_left) len = bytes_left;\n\n            int ret = write_data(ctx, ptr, len);\n            if(ret) return ret;\n\n            bytes_left -= len;\n        }\n    }\n    else\n    {\n        ctx->bytes_encoded += chunk->length;\n        if(ctx->bytes_encoded < chunk->length) return SPNG_EOVERFLOW;\n\n        ctx->bytes_encoded += 12;\n        if(ctx->bytes_encoded < 12) return SPNG_EOVERFLOW;\n\n        ctx->write_ptr += chunk->length + 12;\n    }\n\n    return 0;\n}\n\nstatic int write_chunk(spng_ctx *ctx, const uint8_t type[4], const void *data, size_t length)\n{\n    if(ctx == NULL || type == NULL) return SPNG_EINTERNAL;\n    if(length && data == NULL) return SPNG_EINTERNAL;\n\n    unsigned char *write_ptr;\n\n    int ret = write_header(ctx, type, length, &write_ptr);\n    if(ret) return ret;\n\n    if(length) memcpy(write_ptr, data, length);\n\n    return finish_chunk(ctx);\n}\n\nstatic int write_iend(spng_ctx *ctx)\n{\n    unsigned char iend_chunk[12] = { 0, 0, 0, 0, 73, 69, 78, 68, 174, 66, 96, 130 };\n    return write_data(ctx, iend_chunk, 12);\n}\n\nstatic int write_unknown_chunks(spng_ctx *ctx, enum spng_location location)\n{\n    if(!ctx->stored.unknown) return 0;\n\n    const struct spng_unknown_chunk *chunk = ctx->chunk_list;\n\n    uint32_t i;\n    for(i=0; i < ctx->n_chunks; i++, chunk++)\n    {\n        if(chunk->location != location) continue;\n\n        int ret = write_chunk(ctx, chunk->type, chunk->data, chunk->length);\n        if(ret) return ret;\n    }\n\n    return 0;\n}\n\n/* Read and check the current chunk's crc,\n   returns -SPNG_CRC_DISCARD if the chunk should be discarded */\nstatic inline int read_and_check_crc(spng_ctx *ctx)\n{\n    if(ctx == NULL) return SPNG_EINTERNAL;\n\n    int ret;\n    ret = read_data(ctx, 4);\n    if(ret) return ret;\n\n    ctx->current_chunk.crc = read_u32(ctx->data);\n\n    if(ctx->skip_crc) return 0;\n\n    if(ctx->cur_actual_crc != ctx->current_chunk.crc)\n    {\n        if(is_critical_chunk(&ctx->current_chunk))\n        {\n            if(ctx->crc_action_critical == SPNG_CRC_USE) return 0;\n        }\n        else\n        {\n            if(ctx->crc_action_ancillary == SPNG_CRC_USE) return 0;\n            if(ctx->crc_action_ancillary == SPNG_CRC_DISCARD) return -SPNG_CRC_DISCARD;\n        }\n\n        return SPNG_ECHUNK_CRC;\n    }\n\n    return 0;\n}\n\n/* Read and validate the current chunk's crc and the next chunk header */\nstatic inline int read_header(spng_ctx *ctx)\n{\n    if(ctx == NULL) return SPNG_EINTERNAL;\n\n    int ret;\n    struct spng_chunk chunk = { 0 };\n\n    ret = read_and_check_crc(ctx);\n    if(ret)\n    {\n        if(ret == -SPNG_CRC_DISCARD)\n        {\n            ctx->discard = 1;\n        }\n        else return ret;\n    }\n\n    ret = read_data(ctx, 8);\n    if(ret) return ret;\n\n    chunk.offset = ctx->bytes_read - 8;\n\n    chunk.length = read_u32(ctx->data);\n\n    memcpy(&chunk.type, ctx->data + 4, 4);\n\n    if(chunk.length > spng_u32max) return SPNG_ECHUNK_STDLEN;\n\n    ctx->cur_chunk_bytes_left = chunk.length;\n\n    if(is_critical_chunk(&chunk) && ctx->crc_action_critical == SPNG_CRC_USE) ctx->skip_crc = 1;\n    else if(ctx->crc_action_ancillary == SPNG_CRC_USE) ctx->skip_crc = 1;\n    else ctx->skip_crc = 0;\n\n    if(!ctx->skip_crc)\n    {\n        ctx->cur_actual_crc = crc32(0, NULL, 0);\n        ctx->cur_actual_crc = crc32(ctx->cur_actual_crc, chunk.type, 4);\n    }\n\n    ctx->current_chunk = chunk;\n\n    return 0;\n}\n\n/* Read chunk bytes and update crc */\nstatic int read_chunk_bytes(spng_ctx *ctx, uint32_t bytes)\n{\n    if(ctx == NULL) return SPNG_EINTERNAL;\n    if(!ctx->cur_chunk_bytes_left || !bytes) return SPNG_EINTERNAL;\n    if(bytes > ctx->cur_chunk_bytes_left) return SPNG_EINTERNAL; /* XXX: more specific error? */\n\n    int ret;\n\n    ret = read_data(ctx, bytes);\n    if(ret) return ret;\n\n    if(!ctx->skip_crc) ctx->cur_actual_crc = crc32(ctx->cur_actual_crc, ctx->data, bytes);\n\n    ctx->cur_chunk_bytes_left -= bytes;\n\n    return ret;\n}\n\n/* read_chunk_bytes() + read_data() with custom output buffer */\nstatic int read_chunk_bytes2(spng_ctx *ctx, void *out, uint32_t bytes)\n{\n    if(ctx == NULL) return SPNG_EINTERNAL;\n    if(!ctx->cur_chunk_bytes_left || !bytes) return SPNG_EINTERNAL;\n    if(bytes > ctx->cur_chunk_bytes_left) return SPNG_EINTERNAL; /* XXX: more specific error? */\n\n    int ret;\n    uint32_t len = bytes;\n\n    if(ctx->streaming && len > SPNG_READ_SIZE) len = SPNG_READ_SIZE;\n\n    while(bytes)\n    {\n        if(len > bytes) len = bytes;\n\n        ret = ctx->read_fn(ctx, ctx->stream_user_ptr, out, len);\n        if(ret) return ret;\n\n        if(!ctx->streaming) memcpy(out, ctx->data, len);\n\n        ctx->bytes_read += len;\n        if(ctx->bytes_read < len) return SPNG_EOVERFLOW;\n\n        if(!ctx->skip_crc) ctx->cur_actual_crc = crc32(ctx->cur_actual_crc, out, len);\n\n        ctx->cur_chunk_bytes_left -= len;\n\n        out = (char*)out + len;\n        bytes -= len;\n        len = SPNG_READ_SIZE;\n    }\n\n    return 0;\n}\n\nstatic int discard_chunk_bytes(spng_ctx *ctx, uint32_t bytes)\n{\n    if(ctx == NULL) return SPNG_EINTERNAL;\n    if(!bytes) return 0;\n\n    int ret;\n\n    if(ctx->streaming) /* Do small, consecutive reads */\n    {\n        while(bytes)\n        {\n            uint32_t len = SPNG_READ_SIZE;\n\n            if(len > bytes) len = bytes;\n\n            ret = read_chunk_bytes(ctx, len);\n            if(ret) return ret;\n\n            bytes -= len;\n        }\n    }\n    else\n    {\n        ret = read_chunk_bytes(ctx, bytes);\n        if(ret) return ret;\n    }\n\n    return 0;\n}\n\nstatic int spng__inflate_init(spng_ctx *ctx, int window_bits)\n{\n    if(ctx->zstream.state) inflateEnd(&ctx->zstream);\n\n    ctx->inflate = 1;\n\n    ctx->zstream.zalloc = spng__zalloc;\n    ctx->zstream.zfree = spng__zfree;\n    ctx->zstream.opaque = ctx;\n\n    if(inflateInit2(&ctx->zstream, window_bits) != Z_OK) return SPNG_EZLIB_INIT;\n\n#if ZLIB_VERNUM >= 0x1290 && !defined(SPNG_USE_MINIZ)\n\n    int validate = 1;\n\n    if(ctx->flags & SPNG_CTX_IGNORE_ADLER32) validate = 0;\n\n    if(is_critical_chunk(&ctx->current_chunk))\n    {\n        if(ctx->crc_action_critical == SPNG_CRC_USE) validate = 0;\n    }\n    else /* ancillary */\n    {\n        if(ctx->crc_action_ancillary == SPNG_CRC_USE) validate = 0;\n    }\n\n    if(inflateValidate(&ctx->zstream, validate)) return SPNG_EZLIB_INIT;\n\n#else /* This requires zlib >= 1.2.11 */\n    #pragma message (\"inflateValidate() not available, SPNG_CTX_IGNORE_ADLER32 will be ignored\")\n#endif\n\n    return 0;\n}\n\nstatic int spng__deflate_init(spng_ctx *ctx, struct spng__zlib_options *options)\n{\n    if(ctx->zstream.state) deflateEnd(&ctx->zstream);\n\n    ctx->deflate = 1;\n\n    z_stream *zstream = &ctx->zstream;\n    zstream->zalloc = spng__zalloc;\n    zstream->zfree = spng__zfree;\n    zstream->opaque = ctx;\n    zstream->data_type = options->data_type;\n\n    int ret = deflateInit2(zstream, options->compression_level, Z_DEFLATED, options->window_bits, options->mem_level, options->strategy);\n\n    if(ret != Z_OK) return SPNG_EZLIB_INIT;\n\n    return 0;\n}\n\n/* Inflate a zlib stream starting with start_buf if non-NULL,\n   continuing from the datastream till an end marker,\n   allocating and writing the inflated stream to *out,\n   leaving \"extra\" bytes at the end, final buffer length is *len.\n\n   Takes into account the chunk size and cache limits.\n*/\nstatic int spng__inflate_stream(spng_ctx *ctx, char **out, size_t *len, size_t extra, const void *start_buf, size_t start_len)\n{\n    int ret = spng__inflate_init(ctx, 15);\n    if(ret) return ret;\n\n    size_t max = ctx->chunk_cache_limit - ctx->chunk_cache_usage;\n\n    if(ctx->max_chunk_size < max) max = ctx->max_chunk_size;\n\n    if(extra > max) return SPNG_ECHUNK_LIMITS;\n    max -= extra;\n\n    uint32_t read_size;\n    size_t size = 8 * 1024;\n    void *t, *buf = spng__malloc(ctx, size);\n\n    if(buf == NULL) return SPNG_EMEM;\n\n    z_stream *stream = &ctx->zstream;\n\n    if(start_buf != NULL && start_len)\n    {\n        stream->avail_in = (uInt)start_len;\n        stream->next_in = start_buf;\n    }\n    else\n    {\n        stream->avail_in = 0;\n        stream->next_in = NULL;\n    }\n\n    stream->avail_out = (uInt)size;\n    stream->next_out = buf;\n\n    while(ret != Z_STREAM_END)\n    {\n        ret = inflate(stream, Z_NO_FLUSH);\n\n        if(ret == Z_STREAM_END) break;\n\n        if(ret != Z_OK && ret != Z_BUF_ERROR)\n        {\n            ret = SPNG_EZLIB;\n            goto err;\n        }\n\n        if(!stream->avail_out) /* Resize buffer */\n        {\n            /* overflow or reached chunk/cache limit */\n            if( (2 > SIZE_MAX / size) || (size > max / 2) )\n            {\n                ret = SPNG_ECHUNK_LIMITS;\n                goto err;\n            }\n\n            size *= 2;\n\n            t = spng__realloc(ctx, buf, size);\n            if(t == NULL) goto mem;\n\n            buf = t;\n\n            stream->avail_out = (uInt)size / 2;\n            stream->next_out = (unsigned char*)buf + size / 2;\n        }\n        else if(!stream->avail_in) /* Read more chunk bytes */\n        {\n            read_size = ctx->cur_chunk_bytes_left;\n            if(ctx->streaming && read_size > SPNG_READ_SIZE) read_size = SPNG_READ_SIZE;\n\n            ret = read_chunk_bytes(ctx, read_size);\n\n            if(ret)\n            {\n                if(!read_size) ret = SPNG_EZLIB;\n\n                goto err;\n            }\n\n            stream->avail_in = read_size;\n            stream->next_in = ctx->data;\n        }\n    }\n\n    size = stream->total_out;\n\n    if(!size)\n    {\n        ret = SPNG_EZLIB;\n        goto err;\n    }\n\n    size += extra;\n    if(size < extra) goto mem;\n\n    t = spng__realloc(ctx, buf, size);\n    if(t == NULL) goto mem;\n\n    buf = t;\n\n    (void)increase_cache_usage(ctx, size, 0);\n\n    *out = buf;\n    *len = size;\n\n    return 0;\n\nmem:\n    ret = SPNG_EMEM;\nerr:\n    spng__free(ctx, buf);\n    return ret;\n}\n\n/* Read at least one byte from the IDAT stream */\nstatic int read_idat_bytes(spng_ctx *ctx, uint32_t *bytes_read)\n{\n    if(ctx == NULL || bytes_read == NULL) return SPNG_EINTERNAL;\n    if(memcmp(ctx->current_chunk.type, type_idat, 4)) return SPNG_EIDAT_TOO_SHORT;\n\n    int ret;\n    uint32_t len;\n\n    while(!ctx->cur_chunk_bytes_left)\n    {\n        ret = read_header(ctx);\n        if(ret) return ret;\n\n        if(memcmp(ctx->current_chunk.type, type_idat, 4)) return SPNG_EIDAT_TOO_SHORT;\n    }\n\n    if(ctx->streaming)\n    {/* TODO: estimate bytes to read for progressive reads */\n        len = SPNG_READ_SIZE;\n        if(len > ctx->cur_chunk_bytes_left) len = ctx->cur_chunk_bytes_left;\n    }\n    else len = ctx->current_chunk.length;\n\n    ret = read_chunk_bytes(ctx, len);\n\n    *bytes_read = len;\n\n    return ret;\n}\n\nstatic int read_scanline_bytes(spng_ctx *ctx, unsigned char *dest, size_t len)\n{\n    if(ctx == NULL || dest == NULL) return SPNG_EINTERNAL;\n\n    int ret = Z_OK;\n    uint32_t bytes_read;\n\n    z_stream *zstream = &ctx->zstream;\n\n    zstream->avail_out = (uInt)len;\n    zstream->next_out = dest;\n\n    while(zstream->avail_out != 0)\n    {\n        ret = inflate(zstream, Z_NO_FLUSH);\n\n        if(ret == Z_OK) continue;\n\n        if(ret == Z_STREAM_END) /* Reached an end-marker */\n        {\n            if(zstream->avail_out != 0) return SPNG_EIDAT_TOO_SHORT;\n        }\n        else if(ret == Z_BUF_ERROR) /* Read more IDAT bytes */\n        {\n            ret = read_idat_bytes(ctx, &bytes_read);\n            if(ret) return ret;\n\n            zstream->avail_in = bytes_read;\n            zstream->next_in = ctx->data;\n        }\n        else return SPNG_EIDAT_STREAM;\n    }\n\n    return 0;\n}\n\nstatic uint8_t paeth(uint8_t a, uint8_t b, uint8_t c)\n{\n    int16_t p = a + b - c;\n    int16_t pa = abs(p - a);\n    int16_t pb = abs(p - b);\n    int16_t pc = abs(p - c);\n\n    if(pa <= pb && pa <= pc) return a;\n    else if(pb <= pc) return b;\n\n    return c;\n}\n\nSPNG_TARGET_CLONES(\"default,avx2\")\nstatic void defilter_up(size_t bytes, unsigned char *row, const unsigned char *prev)\n{\n    size_t i;\n    for(i=0; i < bytes; i++)\n    {\n        row[i] += prev[i];\n    }\n}\n\n/* Defilter *scanline in-place.\n   *prev_scanline and *scanline should point to the first pixel,\n   scanline_width is the width of the scanline including the filter byte.\n*/\nstatic int defilter_scanline(const unsigned char *prev_scanline, unsigned char *scanline,\n                             size_t scanline_width, unsigned bytes_per_pixel, unsigned filter)\n{\n    if(prev_scanline == NULL || scanline == NULL || !scanline_width) return SPNG_EINTERNAL;\n\n    size_t i;\n    scanline_width--;\n\n    if(filter == 0) return 0;\n\n#ifndef SPNG_DISABLE_OPT\n    if(filter == SPNG_FILTER_UP) goto no_opt;\n\n    if(bytes_per_pixel == 4)\n    {\n        if(filter == SPNG_FILTER_SUB)\n            defilter_sub4(scanline_width, scanline);\n        else if(filter == SPNG_FILTER_AVERAGE)\n            defilter_avg4(scanline_width, scanline, prev_scanline);\n        else if(filter == SPNG_FILTER_PAETH)\n            defilter_paeth4(scanline_width, scanline, prev_scanline);\n        else return SPNG_EFILTER;\n\n        return 0;\n    }\n    else if(bytes_per_pixel == 3)\n    {\n        if(filter == SPNG_FILTER_SUB)\n            defilter_sub3(scanline_width, scanline);\n        else if(filter == SPNG_FILTER_AVERAGE)\n            defilter_avg3(scanline_width, scanline, prev_scanline);\n        else if(filter == SPNG_FILTER_PAETH)\n            defilter_paeth3(scanline_width, scanline, prev_scanline);\n        else return SPNG_EFILTER;\n\n        return 0;\n    }\nno_opt:\n#endif\n\n    if(filter == SPNG_FILTER_UP)\n    {\n        defilter_up(scanline_width, scanline, prev_scanline);\n        return 0;\n    }\n\n    for(i=0; i < scanline_width; i++)\n    {\n        uint8_t x, a, b, c;\n\n        if(i >= bytes_per_pixel)\n        {\n            a = scanline[i - bytes_per_pixel];\n            b = prev_scanline[i];\n            c = prev_scanline[i - bytes_per_pixel];\n        }\n        else /* First pixel in row */\n        {\n            a = 0;\n            b = prev_scanline[i];\n            c = 0;\n        }\n\n        x = scanline[i];\n\n        switch(filter)\n        {\n            case SPNG_FILTER_SUB:\n            {\n                x = x + a;\n                break;\n            }\n            case SPNG_FILTER_AVERAGE:\n            {\n                uint16_t avg = (a + b) / 2;\n                x = x + avg;\n                break;\n            }\n            case SPNG_FILTER_PAETH:\n            {\n                x = x + paeth(a,b,c);\n                break;\n            }\n        }\n\n        scanline[i] = x;\n    }\n\n    return 0;\n}\n\nstatic int filter_scanline(unsigned char *filtered, const unsigned char *prev_scanline, const unsigned char *scanline,\n                           size_t scanline_width, unsigned bytes_per_pixel, const unsigned filter)\n{\n    if(prev_scanline == NULL || scanline == NULL || scanline_width <= 1) return SPNG_EINTERNAL;\n\n    if(filter > 4) return SPNG_EFILTER;\n    if(filter == 0) return 0;\n\n    scanline_width--;\n\n    uint32_t i;\n    for(i=0; i < scanline_width; i++)\n    {\n        uint8_t x, a, b, c;\n\n        if(i >= bytes_per_pixel)\n        {\n            a = scanline[i - bytes_per_pixel];\n            b = prev_scanline[i];\n            c = prev_scanline[i - bytes_per_pixel];\n        }\n        else /* first pixel in row */\n        {\n            a = 0;\n            b = prev_scanline[i];\n            c = 0;\n        }\n\n        x = scanline[i];\n\n        switch(filter)\n        {\n            case SPNG_FILTER_SUB:\n            {\n                x = x - a;\n                break;\n            }\n            case SPNG_FILTER_UP:\n            {\n                x = x - b;\n                break;\n            }\n            case SPNG_FILTER_AVERAGE:\n            {\n                uint16_t avg = (a + b) / 2;\n                x = x - avg;\n                break;\n            }\n            case SPNG_FILTER_PAETH:\n            {\n                x = x - paeth(a,b,c);\n                break;\n            }\n        }\n\n        filtered[i] = x;\n    }\n\n    return 0;\n}\n\nstatic int32_t filter_sum(const unsigned char *prev_scanline, const unsigned char *scanline,\n                          size_t size, unsigned bytes_per_pixel, const unsigned filter)\n{\n    /* prevent potential over/underflow, bails out at a width of ~8M pixels for RGBA8 */\n    if(size > (INT32_MAX / 128)) return INT32_MAX;\n\n    uint32_t i;\n    int32_t sum = 0;\n    uint8_t x, a, b, c;\n\n    for(i=0; i < size; i++)\n    {\n        if(i >= bytes_per_pixel)\n        {\n            a = scanline[i - bytes_per_pixel];\n            b = prev_scanline[i];\n            c = prev_scanline[i - bytes_per_pixel];\n        }\n        else /* first pixel in row */\n        {\n            a = 0;\n            b = prev_scanline[i];\n            c = 0;\n        }\n\n        x = scanline[i];\n\n        switch(filter)\n        {\n            case SPNG_FILTER_NONE:\n            {\n                break;\n            }\n            case SPNG_FILTER_SUB:\n            {\n                x = x - a;\n                break;\n            }\n            case SPNG_FILTER_UP:\n            {\n                x = x - b;\n                break;\n            }\n            case SPNG_FILTER_AVERAGE:\n            {\n                uint16_t avg = (a + b) / 2;\n                x = x - avg;\n                break;\n            }\n            case SPNG_FILTER_PAETH:\n            {\n                x = x - paeth(a,b,c);\n                break;\n            }\n        }\n\n        sum += 128 - abs((int)x - 128);\n    }\n\n    return sum;\n}\n\nstatic unsigned get_best_filter(const unsigned char *prev_scanline, const unsigned char *scanline,\n                                size_t scanline_width, unsigned bytes_per_pixel, const int choices)\n{\n    if(!choices) return SPNG_FILTER_NONE;\n\n    scanline_width--;\n\n    int i;\n    unsigned int best_filter = 0;\n    enum spng_filter_choice flag;\n    int32_t sum, best_score = INT32_MAX;\n    int32_t filter_scores[5] = { INT32_MAX, INT32_MAX, INT32_MAX, INT32_MAX, INT32_MAX };\n\n    if( !(choices & (choices - 1)) )\n    {/* only one choice/bit is set */\n        for(i=0; i < 5; i++)\n        {\n            if(choices == 1 << (i + 3)) return i;\n        }\n    }\n\n    for(i=0; i < 5; i++)\n    {\n        flag = 1 << (i + 3);\n\n        if(choices & flag) sum = filter_sum(prev_scanline, scanline, scanline_width, bytes_per_pixel, i);\n        else continue;\n\n        filter_scores[i] = abs(sum);\n\n        if(filter_scores[i] < best_score)\n        {\n            best_score = filter_scores[i];\n            best_filter = i;\n        }\n    }\n\n    return best_filter;\n}\n\n/* Scale \"sbits\" significant bits in \"sample\" from \"bit_depth\" to \"target\"\n\n   \"bit_depth\" must be a valid PNG depth\n   \"sbits\" must be less than or equal to \"bit_depth\"\n   \"target\" must be between 1 and 16\n*/\nstatic uint16_t sample_to_target(uint16_t sample, unsigned bit_depth, unsigned sbits, unsigned target)\n{\n    if(bit_depth == sbits)\n    {\n        if(target == sbits) return sample; /* No scaling */\n    }/* bit_depth > sbits */\n    else sample = sample >> (bit_depth - sbits); /* Shift significant bits to bottom */\n\n    /* Downscale */\n    if(target < sbits) return sample >> (sbits - target);\n\n    /* Upscale using left bit replication */\n    int8_t shift_amount = target - sbits;\n    uint16_t sample_bits = sample;\n    sample = 0;\n\n    while(shift_amount >= 0)\n    {\n        sample = sample | (sample_bits << shift_amount);\n        shift_amount -= sbits;\n    }\n\n    int8_t partial = shift_amount + (int8_t)sbits;\n\n    if(partial != 0) sample = sample | (sample_bits >> abs(shift_amount));\n\n    return sample;\n}\n\nstatic inline void gamma_correct_row(unsigned char *row, uint32_t pixels, int fmt, const uint16_t *gamma_lut)\n{\n    uint32_t i;\n\n    if(fmt == SPNG_FMT_RGBA8)\n    {\n        unsigned char *px;\n        for(i=0; i < pixels; i++)\n        {\n            px = row + i * 4;\n\n            px[0] = gamma_lut[px[0]];\n            px[1] = gamma_lut[px[1]];\n            px[2] = gamma_lut[px[2]];\n        }\n    }\n    else if(fmt == SPNG_FMT_RGBA16)\n    {\n        for(i=0; i < pixels; i++)\n        {\n            uint16_t px[4];\n            memcpy(px, row + i * 8, 8);\n\n            px[0] = gamma_lut[px[0]];\n            px[1] = gamma_lut[px[1]];\n            px[2] = gamma_lut[px[2]];\n\n            memcpy(row + i * 8, px, 8);\n        }\n    }\n    else if(fmt == SPNG_FMT_RGB8)\n    {\n        unsigned char *px;\n        for(i=0; i < pixels; i++)\n        {\n            px = row + i * 3;\n\n            px[0] = gamma_lut[px[0]];\n            px[1] = gamma_lut[px[1]];\n            px[2] = gamma_lut[px[2]];\n        }\n    }\n}\n\n/* Apply transparency to output row */\nstatic inline void trns_row(unsigned char *row,\n                            const unsigned char *scanline,\n                            const unsigned char *trns,\n                            unsigned scanline_stride,\n                            struct spng_ihdr *ihdr,\n                            uint32_t pixels,\n                            int fmt)\n{\n    uint32_t i;\n    unsigned row_stride;\n    unsigned depth = ihdr->bit_depth;\n\n    if(fmt == SPNG_FMT_RGBA8)\n    {\n        if(ihdr->color_type == SPNG_COLOR_TYPE_GRAYSCALE) return; /* already applied in the decoding loop */\n\n        row_stride = 4;\n        for(i=0; i < pixels; i++, scanline+=scanline_stride, row+=row_stride)\n        {\n            if(!memcmp(scanline, trns, scanline_stride)) row[3] = 0;\n        }\n    }\n    else if(fmt == SPNG_FMT_RGBA16)\n    {\n        if(ihdr->color_type == SPNG_COLOR_TYPE_GRAYSCALE) return; /* already applied in the decoding loop */\n\n        row_stride = 8;\n        for(i=0; i < pixels; i++, scanline+=scanline_stride, row+=row_stride)\n        {\n            if(!memcmp(scanline, trns, scanline_stride)) memset(row + 6, 0, 2);\n        }\n    }\n    else if(fmt == SPNG_FMT_GA8)\n    {\n        row_stride = 2;\n\n        if(depth == 16)\n        {\n            for(i=0; i < pixels; i++, scanline+=scanline_stride, row+=row_stride)\n            {\n                if(!memcmp(scanline, trns, scanline_stride)) memset(row + 1, 0, 1);\n            }\n        }\n        else /* depth <= 8 */\n        {\n            struct spng__iter iter = spng__iter_init(depth, scanline);\n\n            for(i=0; i < pixels; i++, row+=row_stride)\n            {\n                if(trns[0] == get_sample(&iter)) row[1] = 0;\n            }\n        }\n    }\n    else if(fmt == SPNG_FMT_GA16)\n    {\n        row_stride = 4;\n\n        if(depth == 16)\n        {\n            for(i=0; i< pixels; i++, scanline+=scanline_stride, row+=row_stride)\n            {\n                if(!memcmp(scanline, trns, 2)) memset(row + 2, 0, 2);\n            }\n        }\n        else\n        {\n            struct spng__iter iter = spng__iter_init(depth, scanline);\n\n            for(i=0; i< pixels; i++, row+=row_stride)\n            {\n                if(trns[0] == get_sample(&iter)) memset(row + 2, 0, 2);\n            }\n        }\n    }\n    else return;\n}\n\nstatic inline void scale_row(unsigned char *row, uint32_t pixels, int fmt, unsigned depth, const struct spng_sbit *sbit)\n{\n    uint32_t i;\n\n    if(fmt == SPNG_FMT_RGBA8)\n    {\n        unsigned char px[4];\n        for(i=0; i < pixels; i++)\n        {\n            memcpy(px, row + i * 4, 4);\n\n            px[0] = sample_to_target(px[0], depth, sbit->red_bits, 8);\n            px[1] = sample_to_target(px[1], depth, sbit->green_bits, 8);\n            px[2] = sample_to_target(px[2], depth, sbit->blue_bits, 8);\n            px[3] = sample_to_target(px[3], depth, sbit->alpha_bits, 8);\n\n            memcpy(row + i * 4, px, 4);\n        }\n    }\n    else if(fmt == SPNG_FMT_RGBA16)\n    {\n        uint16_t px[4];\n        for(i=0; i < pixels; i++)\n        {\n            memcpy(px, row + i * 8, 8);\n\n            px[0] = sample_to_target(px[0], depth, sbit->red_bits, 16);\n            px[1] = sample_to_target(px[1], depth, sbit->green_bits, 16);\n            px[2] = sample_to_target(px[2], depth, sbit->blue_bits, 16);\n            px[3] = sample_to_target(px[3], depth, sbit->alpha_bits, 16);\n\n            memcpy(row + i * 8, px, 8);\n        }\n    }\n    else if(fmt == SPNG_FMT_RGB8)\n    {\n        unsigned char px[4];\n        for(i=0; i < pixels; i++)\n        {\n            memcpy(px, row + i * 3, 3);\n\n            px[0] = sample_to_target(px[0], depth, sbit->red_bits, 8);\n            px[1] = sample_to_target(px[1], depth, sbit->green_bits, 8);\n            px[2] = sample_to_target(px[2], depth, sbit->blue_bits, 8);\n\n            memcpy(row + i * 3, px, 3);\n        }\n    }\n    else if(fmt == SPNG_FMT_G8)\n    {\n        for(i=0; i < pixels; i++)\n        {\n            row[i] = sample_to_target(row[i], depth, sbit->grayscale_bits, 8);\n        }\n    }\n    else if(fmt == SPNG_FMT_GA8)\n    {\n        for(i=0; i < pixels; i++)\n        {\n            row[i*2] = sample_to_target(row[i*2], depth, sbit->grayscale_bits, 8);\n        }\n    }\n}\n\n/* Expand to *row using 8-bit palette indices from *scanline */\nstatic void expand_row(unsigned char *row,\n                       const unsigned char *scanline,\n                       const union spng__decode_plte *decode_plte,\n                       uint32_t width,\n                       int fmt)\n{\n    uint32_t i = 0;\n    unsigned char *px;\n    unsigned char entry;\n    const struct spng_plte_entry *plte = decode_plte->rgba;\n\n#if defined(SPNG_ARM)\n    if(fmt == SPNG_FMT_RGBA8) i = expand_palette_rgba8_neon(row, scanline, decode_plte->raw, width);\n    else if(fmt == SPNG_FMT_RGB8)\n    {\n        i = expand_palette_rgb8_neon(row, scanline, decode_plte->raw, width);\n\n        for(; i < width; i++)\n        {/* In this case the LUT is 3 bytes packed */\n            px = row + i * 3;\n            entry = scanline[i];\n            px[0] = decode_plte->raw[entry * 3 + 0];\n            px[1] = decode_plte->raw[entry * 3 + 1];\n            px[2] = decode_plte->raw[entry * 3 + 2];\n        }\n        return;\n    }\n#endif\n\n    if(fmt == SPNG_FMT_RGBA8)\n    {\n        for(; i < width; i++)\n        {\n            px = row + i * 4;\n            entry = scanline[i];\n            px[0] = plte[entry].red;\n            px[1] = plte[entry].green;\n            px[2] = plte[entry].blue;\n            px[3] = plte[entry].alpha;\n        }\n    }\n    else if(fmt == SPNG_FMT_RGB8)\n    {\n        for(; i < width; i++)\n        {\n            px = row + i * 3;\n            entry = scanline[i];\n            px[0] = plte[entry].red;\n            px[1] = plte[entry].green;\n            px[2] = plte[entry].blue;\n        }\n    }\n}\n\n/* Unpack 1/2/4/8-bit samples to G8/GA8/GA16 or G16 -> GA16 */\nstatic void unpack_scanline(unsigned char *out, const unsigned char *scanline, uint32_t width, unsigned bit_depth, int fmt)\n{\n    struct spng__iter iter = spng__iter_init(bit_depth, scanline);\n    uint32_t i;\n    uint16_t sample, alpha = 65535;\n\n\n    if(fmt == SPNG_FMT_GA8) goto ga8;\n    else if(fmt == SPNG_FMT_GA16) goto ga16;\n\n    /* 1/2/4-bit -> 8-bit */\n    for(i=0; i < width; i++) out[i] = get_sample(&iter);\n\n    return;\n\nga8:\n    /* 1/2/4/8-bit -> GA8 */\n    for(i=0; i < width; i++)\n    {\n        out[i*2] = get_sample(&iter);\n        out[i*2 + 1] = 255;\n    }\n\n    return;\n\nga16:\n\n    /* 16 -> GA16 */\n    if(bit_depth == 16)\n    {\n        for(i=0; i < width; i++)\n        {\n            memcpy(out + i * 4, scanline + i * 2, 2);\n            memcpy(out + i * 4 + 2, &alpha, 2);\n        }\n        return;\n    }\n\n     /* 1/2/4/8-bit -> GA16 */\n    for(i=0; i < width; i++)\n    {\n        sample = get_sample(&iter);\n        memcpy(out + i * 4, &sample, 2);\n        memcpy(out + i * 4 + 2, &alpha, 2);\n    }\n}\n\nstatic int check_ihdr(const struct spng_ihdr *ihdr, uint32_t max_width, uint32_t max_height)\n{\n    if(ihdr->width > spng_u32max || !ihdr->width) return SPNG_EWIDTH;\n    if(ihdr->height > spng_u32max || !ihdr->height) return SPNG_EHEIGHT;\n\n    if(ihdr->width > max_width) return SPNG_EUSER_WIDTH;\n    if(ihdr->height > max_height) return SPNG_EUSER_HEIGHT;\n\n    switch(ihdr->color_type)\n    {\n        case SPNG_COLOR_TYPE_GRAYSCALE:\n        {\n            if( !(ihdr->bit_depth == 1 || ihdr->bit_depth == 2 ||\n                  ihdr->bit_depth == 4 || ihdr->bit_depth == 8 ||\n                  ihdr->bit_depth == 16) )\n                  return SPNG_EBIT_DEPTH;\n\n            break;\n        }\n        case SPNG_COLOR_TYPE_TRUECOLOR:\n        case SPNG_COLOR_TYPE_GRAYSCALE_ALPHA:\n        case SPNG_COLOR_TYPE_TRUECOLOR_ALPHA:\n        {\n            if( !(ihdr->bit_depth == 8 || ihdr->bit_depth == 16) )\n                return SPNG_EBIT_DEPTH;\n\n            break;\n        }\n        case SPNG_COLOR_TYPE_INDEXED:\n        {\n            if( !(ihdr->bit_depth == 1 || ihdr->bit_depth == 2 ||\n                  ihdr->bit_depth == 4 || ihdr->bit_depth == 8) )\n                return SPNG_EBIT_DEPTH;\n\n            break;\n        }\n        default: return SPNG_ECOLOR_TYPE;\n    }\n\n    if(ihdr->compression_method) return SPNG_ECOMPRESSION_METHOD;\n    if(ihdr->filter_method) return SPNG_EFILTER_METHOD;\n\n    if(ihdr->interlace_method > 1) return SPNG_EINTERLACE_METHOD;\n\n    return 0;\n}\n\nstatic int check_plte(const struct spng_plte *plte, const struct spng_ihdr *ihdr)\n{\n    if(plte == NULL || ihdr == NULL) return 1;\n\n    if(plte->n_entries == 0) return 1;\n    if(plte->n_entries > 256) return 1;\n\n    if(ihdr->color_type == SPNG_COLOR_TYPE_INDEXED)\n    {\n        if(plte->n_entries > (1U << ihdr->bit_depth)) return 1;\n    }\n\n    return 0;\n}\n\nstatic int check_sbit(const struct spng_sbit *sbit, const struct spng_ihdr *ihdr)\n{\n    if(sbit == NULL || ihdr == NULL) return 1;\n\n    if(ihdr->color_type == 0)\n    {\n        if(sbit->grayscale_bits == 0) return SPNG_ESBIT;\n        if(sbit->grayscale_bits > ihdr->bit_depth) return SPNG_ESBIT;\n    }\n    else if(ihdr->color_type == 2 || ihdr->color_type == 3)\n    {\n        if(sbit->red_bits == 0) return SPNG_ESBIT;\n        if(sbit->green_bits == 0) return SPNG_ESBIT;\n        if(sbit->blue_bits == 0) return SPNG_ESBIT;\n\n        uint8_t bit_depth;\n        if(ihdr->color_type == 3) bit_depth = 8;\n        else bit_depth = ihdr->bit_depth;\n\n        if(sbit->red_bits > bit_depth) return SPNG_ESBIT;\n        if(sbit->green_bits > bit_depth) return SPNG_ESBIT;\n        if(sbit->blue_bits > bit_depth) return SPNG_ESBIT;\n    }\n    else if(ihdr->color_type == 4)\n    {\n        if(sbit->grayscale_bits == 0) return SPNG_ESBIT;\n        if(sbit->alpha_bits == 0) return SPNG_ESBIT;\n\n        if(sbit->grayscale_bits > ihdr->bit_depth) return SPNG_ESBIT;\n        if(sbit->alpha_bits > ihdr->bit_depth) return SPNG_ESBIT;\n    }\n    else if(ihdr->color_type == 6)\n    {\n        if(sbit->red_bits == 0) return SPNG_ESBIT;\n        if(sbit->green_bits == 0) return SPNG_ESBIT;\n        if(sbit->blue_bits == 0) return SPNG_ESBIT;\n        if(sbit->alpha_bits == 0) return SPNG_ESBIT;\n\n        if(sbit->red_bits > ihdr->bit_depth) return SPNG_ESBIT;\n        if(sbit->green_bits > ihdr->bit_depth) return SPNG_ESBIT;\n        if(sbit->blue_bits > ihdr->bit_depth) return SPNG_ESBIT;\n        if(sbit->alpha_bits > ihdr->bit_depth) return SPNG_ESBIT;\n    }\n\n    return 0;\n}\n\nstatic int check_chrm_int(const struct spng_chrm_int *chrm_int)\n{\n    if(chrm_int == NULL) return 1;\n\n    if(chrm_int->white_point_x > spng_u32max ||\n       chrm_int->white_point_y > spng_u32max ||\n       chrm_int->red_x > spng_u32max ||\n       chrm_int->red_y > spng_u32max ||\n       chrm_int->green_x  > spng_u32max ||\n       chrm_int->green_y  > spng_u32max ||\n       chrm_int->blue_x > spng_u32max ||\n       chrm_int->blue_y > spng_u32max) return SPNG_ECHRM;\n\n    return 0;\n}\n\nstatic int check_phys(const struct spng_phys *phys)\n{\n    if(phys == NULL) return 1;\n\n    if(phys->unit_specifier > 1) return SPNG_EPHYS;\n\n    if(phys->ppu_x > spng_u32max) return SPNG_EPHYS;\n    if(phys->ppu_y > spng_u32max) return SPNG_EPHYS;\n\n    return 0;\n}\n\nstatic int check_time(const struct spng_time *time)\n{\n    if(time == NULL) return 1;\n\n    if(time->month == 0 || time->month > 12) return 1;\n    if(time->day == 0 || time->day > 31) return 1;\n    if(time->hour > 23) return 1;\n    if(time->minute > 59) return 1;\n    if(time->second > 60) return 1;\n\n    return 0;\n}\n\nstatic int check_offs(const struct spng_offs *offs)\n{\n    if(offs == NULL) return 1;\n\n    if(offs->unit_specifier > 1) return 1;\n\n    return 0;\n}\n\nstatic int check_exif(const struct spng_exif *exif)\n{\n    if(exif == NULL) return 1;\n    if(exif->data == NULL) return 1;\n\n    if(exif->length < 4) return SPNG_ECHUNK_SIZE;\n    if(exif->length > spng_u32max) return SPNG_ECHUNK_STDLEN;\n\n    const uint8_t exif_le[4] = { 73, 73, 42, 0 };\n    const uint8_t exif_be[4] = { 77, 77, 0, 42 };\n\n    if(memcmp(exif->data, exif_le, 4) && memcmp(exif->data, exif_be, 4)) return 1;\n\n    return 0;\n}\n\n/* Validate PNG keyword */\nstatic int check_png_keyword(const char *str)\n{\n    if(str == NULL) return 1;\n    size_t len = strlen(str);\n    const char *end = str + len;\n\n    if(!len) return 1;\n    if(len > 79) return 1;\n    if(str[0] == ' ') return 1; /* Leading space */\n    if(end[-1] == ' ') return 1; /* Trailing space */\n    if(strstr(str, \"  \") != NULL) return 1; /* Consecutive spaces */\n\n    uint8_t c;\n    while(str != end)\n    {\n        memcpy(&c, str, 1);\n\n        if( (c >= 32 && c <= 126) || (c >= 161) ) str++;\n        else return 1; /* Invalid character */\n    }\n\n    return 0;\n}\n\n/* Validate PNG text *str up to 'len' bytes */\nstatic int check_png_text(const char *str, size_t len)\n{/* XXX: are consecutive newlines permitted? */\n    if(str == NULL || len == 0) return 1;\n\n    uint8_t c;\n    size_t i = 0;\n    while(i < len)\n    {\n        memcpy(&c, str + i, 1);\n\n        if( (c >= 32 && c <= 126) || (c >= 161) || c == 10) i++;\n        else return 1; /* Invalid character */\n    }\n\n    return 0;\n}\n\n/* Returns non-zero for standard chunks which are stored without allocating memory */\nstatic int is_small_chunk(uint8_t type[4])\n{\n    if(!memcmp(type, type_plte, 4)) return 1;\n    else if(!memcmp(type, type_chrm, 4)) return 1;\n    else if(!memcmp(type, type_gama, 4)) return 1;\n    else if(!memcmp(type, type_sbit, 4)) return 1;\n    else if(!memcmp(type, type_srgb, 4)) return 1;\n    else if(!memcmp(type, type_bkgd, 4)) return 1;\n    else if(!memcmp(type, type_trns, 4)) return 1;\n    else if(!memcmp(type, type_hist, 4)) return 1;\n    else if(!memcmp(type, type_phys, 4)) return 1;\n    else if(!memcmp(type, type_time, 4)) return 1;\n    else if(!memcmp(type, type_offs, 4)) return 1;\n    else return 0;\n}\n\nstatic int read_ihdr(spng_ctx *ctx)\n{\n    int ret;\n    struct spng_chunk *chunk = &ctx->current_chunk;\n    const unsigned char *data;\n\n    chunk->offset = 8;\n    chunk->length = 13;\n    size_t sizeof_sig_ihdr = 29;\n\n    ret = read_data(ctx, sizeof_sig_ihdr);\n    if(ret) return ret;\n\n    data = ctx->data;\n\n    if(memcmp(data, spng_signature, sizeof(spng_signature))) return SPNG_ESIGNATURE;\n\n    chunk->length = read_u32(data + 8);\n    memcpy(&chunk->type, data + 12, 4);\n\n    if(chunk->length != 13) return SPNG_EIHDR_SIZE;\n    if(memcmp(chunk->type, type_ihdr, 4)) return SPNG_ENOIHDR;\n\n    ctx->cur_actual_crc = crc32(0, NULL, 0);\n    ctx->cur_actual_crc = crc32(ctx->cur_actual_crc, data + 12, 17);\n\n    ctx->ihdr.width = read_u32(data + 16);\n    ctx->ihdr.height = read_u32(data + 20);\n    ctx->ihdr.bit_depth = data[24];\n    ctx->ihdr.color_type = data[25];\n    ctx->ihdr.compression_method = data[26];\n    ctx->ihdr.filter_method = data[27];\n    ctx->ihdr.interlace_method = data[28];\n\n    ret = check_ihdr(&ctx->ihdr, ctx->max_width, ctx->max_height);\n    if(ret) return ret;\n\n    ctx->file.ihdr = 1;\n    ctx->stored.ihdr = 1;\n\n    if(ctx->ihdr.bit_depth < 8) ctx->bytes_per_pixel = 1;\n    else ctx->bytes_per_pixel = num_channels(&ctx->ihdr) * (ctx->ihdr.bit_depth / 8);\n\n    ret = calculate_subimages(ctx);\n    if(ret) return ret;\n\n    return 0;\n}\n\nstatic void splt_undo(spng_ctx *ctx)\n{\n    struct spng_splt *splt = &ctx->splt_list[ctx->n_splt - 1];\n\n    spng__free(ctx, splt->entries);\n\n    decrease_cache_usage(ctx, sizeof(struct spng_splt));\n    decrease_cache_usage(ctx, splt->n_entries * sizeof(struct spng_splt_entry));\n\n    splt->entries = NULL;\n\n    ctx->n_splt--;\n}\n\nstatic void text_undo(spng_ctx *ctx)\n{\n    struct spng_text2 *text = &ctx->text_list[ctx->n_text - 1];\n\n    spng__free(ctx, text->keyword);\n    if(text->compression_flag) spng__free(ctx, text->text);\n\n    decrease_cache_usage(ctx, text->cache_usage);\n    decrease_cache_usage(ctx, sizeof(struct spng_text2));\n\n    text->keyword = NULL;\n    text->text = NULL;\n\n    ctx->n_text--;\n}\n\nstatic void chunk_undo(spng_ctx *ctx)\n{\n    struct spng_unknown_chunk *chunk = &ctx->chunk_list[ctx->n_chunks - 1];\n\n    spng__free(ctx, chunk->data);\n\n    decrease_cache_usage(ctx, chunk->length);\n    decrease_cache_usage(ctx, sizeof(struct spng_unknown_chunk));\n\n    chunk->data = NULL;\n\n    ctx->n_chunks--;\n}\n\nstatic int read_non_idat_chunks(spng_ctx *ctx)\n{\n    int ret;\n    struct spng_chunk chunk;\n    const unsigned char *data;\n\n    ctx->discard = 0;\n    ctx->undo = NULL;\n    ctx->prev_stored = ctx->stored;\n\n    while( !(ret = read_header(ctx)))\n    {\n        if(ctx->discard)\n        {\n            if(ctx->undo) ctx->undo(ctx);\n\n            ctx->stored = ctx->prev_stored;\n        }\n\n        ctx->discard = 0;\n        ctx->undo = NULL;\n\n        ctx->prev_stored = ctx->stored;\n        chunk = ctx->current_chunk;\n\n        if(!memcmp(chunk.type, type_idat, 4))\n        {\n            if(ctx->state < SPNG_STATE_FIRST_IDAT)\n            {\n                if(ctx->ihdr.color_type == 3 && !ctx->stored.plte) return SPNG_ENOPLTE;\n\n                ctx->first_idat = chunk;\n                return 0;\n            }\n\n            if(ctx->prev_was_idat)\n            {\n                /* Ignore extra IDAT's */\n                ret = discard_chunk_bytes(ctx, chunk.length);\n                if(ret) return ret;\n\n                continue;\n            }\n            else return SPNG_ECHUNK_POS; /* IDAT chunk not at the end of the IDAT sequence */\n        }\n\n        ctx->prev_was_idat = 0;\n\n        if(is_small_chunk(chunk.type))\n        {\n            /* None of the known chunks can be zero length */\n            if(!chunk.length) return SPNG_ECHUNK_SIZE;\n\n            /* The largest of these chunks is PLTE with 256 entries */\n            ret = read_chunk_bytes(ctx, chunk.length > 768 ? 768 : chunk.length);\n            if(ret) return ret;\n        }\n\n        data = ctx->data;\n\n        if(is_critical_chunk(&chunk))\n        {\n            if(!memcmp(chunk.type, type_plte, 4))\n            {\n                if(ctx->file.trns || ctx->file.hist || ctx->file.bkgd) return SPNG_ECHUNK_POS;\n                if(chunk.length % 3 != 0) return SPNG_ECHUNK_SIZE;\n\n                ctx->plte.n_entries = chunk.length / 3;\n\n                if(check_plte(&ctx->plte, &ctx->ihdr)) return SPNG_ECHUNK_SIZE; /* XXX: EPLTE? */\n\n                size_t i;\n                for(i=0; i < ctx->plte.n_entries; i++)\n                {\n                    ctx->plte.entries[i].red   = data[i * 3];\n                    ctx->plte.entries[i].green = data[i * 3 + 1];\n                    ctx->plte.entries[i].blue  = data[i * 3 + 2];\n                }\n\n                ctx->file.plte = 1;\n                ctx->stored.plte = 1;\n            }\n            else if(!memcmp(chunk.type, type_iend, 4))\n            {\n                if(ctx->state == SPNG_STATE_AFTER_IDAT)\n                {\n                    if(chunk.length) return SPNG_ECHUNK_SIZE;\n\n                    ret = read_and_check_crc(ctx);\n                    if(ret == -SPNG_CRC_DISCARD) ret = 0;\n\n                    return ret;\n                }\n                else return SPNG_ECHUNK_POS;\n            }\n            else if(!memcmp(chunk.type, type_ihdr, 4)) return SPNG_ECHUNK_POS;\n            else return SPNG_ECHUNK_UNKNOWN_CRITICAL;\n        }\n        else if(!memcmp(chunk.type, type_chrm, 4)) /* Ancillary chunks */\n        {\n            if(ctx->file.plte) return SPNG_ECHUNK_POS;\n            if(ctx->state == SPNG_STATE_AFTER_IDAT) return SPNG_ECHUNK_POS;\n            if(ctx->file.chrm) return SPNG_EDUP_CHRM;\n\n            if(chunk.length != 32) return SPNG_ECHUNK_SIZE;\n\n            ctx->chrm_int.white_point_x = read_u32(data);\n            ctx->chrm_int.white_point_y = read_u32(data + 4);\n            ctx->chrm_int.red_x = read_u32(data + 8);\n            ctx->chrm_int.red_y = read_u32(data + 12);\n            ctx->chrm_int.green_x = read_u32(data + 16);\n            ctx->chrm_int.green_y = read_u32(data + 20);\n            ctx->chrm_int.blue_x = read_u32(data + 24);\n            ctx->chrm_int.blue_y = read_u32(data + 28);\n\n            if(check_chrm_int(&ctx->chrm_int)) return SPNG_ECHRM;\n\n            ctx->file.chrm = 1;\n            ctx->stored.chrm = 1;\n        }\n        else if(!memcmp(chunk.type, type_gama, 4))\n        {\n            if(ctx->file.plte) return SPNG_ECHUNK_POS;\n            if(ctx->state == SPNG_STATE_AFTER_IDAT) return SPNG_ECHUNK_POS;\n            if(ctx->file.gama) return SPNG_EDUP_GAMA;\n\n            if(chunk.length != 4) return SPNG_ECHUNK_SIZE;\n\n            ctx->gama = read_u32(data);\n\n            if(!ctx->gama) return SPNG_EGAMA;\n            if(ctx->gama > spng_u32max) return SPNG_EGAMA;\n\n            ctx->file.gama = 1;\n            ctx->stored.gama = 1;\n        }\n        else if(!memcmp(chunk.type, type_sbit, 4))\n        {\n            if(ctx->file.plte) return SPNG_ECHUNK_POS;\n            if(ctx->state == SPNG_STATE_AFTER_IDAT) return SPNG_ECHUNK_POS;\n            if(ctx->file.sbit) return SPNG_EDUP_SBIT;\n\n            if(ctx->ihdr.color_type == 0)\n            {\n                if(chunk.length != 1) return SPNG_ECHUNK_SIZE;\n\n                ctx->sbit.grayscale_bits = data[0];\n            }\n            else if(ctx->ihdr.color_type == 2 || ctx->ihdr.color_type == 3)\n            {\n                if(chunk.length != 3) return SPNG_ECHUNK_SIZE;\n\n                ctx->sbit.red_bits = data[0];\n                ctx->sbit.green_bits = data[1];\n                ctx->sbit.blue_bits = data[2];\n            }\n            else if(ctx->ihdr.color_type == 4)\n            {\n                if(chunk.length != 2) return SPNG_ECHUNK_SIZE;\n\n                ctx->sbit.grayscale_bits = data[0];\n                ctx->sbit.alpha_bits = data[1];\n            }\n            else if(ctx->ihdr.color_type == 6)\n            {\n                if(chunk.length != 4) return SPNG_ECHUNK_SIZE;\n\n                ctx->sbit.red_bits = data[0];\n                ctx->sbit.green_bits = data[1];\n                ctx->sbit.blue_bits = data[2];\n                ctx->sbit.alpha_bits = data[3];\n            }\n\n            if(check_sbit(&ctx->sbit, &ctx->ihdr)) return SPNG_ESBIT;\n\n            ctx->file.sbit = 1;\n            ctx->stored.sbit = 1;\n        }\n        else if(!memcmp(chunk.type, type_srgb, 4))\n        {\n            if(ctx->file.plte) return SPNG_ECHUNK_POS;\n            if(ctx->state == SPNG_STATE_AFTER_IDAT) return SPNG_ECHUNK_POS;\n            if(ctx->file.srgb) return SPNG_EDUP_SRGB;\n\n            if(chunk.length != 1) return SPNG_ECHUNK_SIZE;\n\n            ctx->srgb_rendering_intent = data[0];\n\n            if(ctx->srgb_rendering_intent > 3) return SPNG_ESRGB;\n\n            ctx->file.srgb = 1;\n            ctx->stored.srgb = 1;\n        }\n        else if(!memcmp(chunk.type, type_bkgd, 4))\n        {\n            if(ctx->state == SPNG_STATE_AFTER_IDAT) return SPNG_ECHUNK_POS;\n            if(ctx->file.bkgd) return SPNG_EDUP_BKGD;\n\n            if(ctx->ihdr.color_type == 0 || ctx->ihdr.color_type == 4)\n            {\n                if(chunk.length != 2) return SPNG_ECHUNK_SIZE;\n\n                ctx->bkgd.gray = read_u16(data);\n            }\n            else if(ctx->ihdr.color_type == 2 || ctx->ihdr.color_type == 6)\n            {\n                if(chunk.length != 6) return SPNG_ECHUNK_SIZE;\n\n                ctx->bkgd.red = read_u16(data);\n                ctx->bkgd.green = read_u16(data + 2);\n                ctx->bkgd.blue = read_u16(data + 4);\n            }\n            else if(ctx->ihdr.color_type == 3)\n            {\n                if(chunk.length != 1) return SPNG_ECHUNK_SIZE;\n                if(!ctx->file.plte) return SPNG_EBKGD_NO_PLTE;\n\n                ctx->bkgd.plte_index = data[0];\n                if(ctx->bkgd.plte_index >= ctx->plte.n_entries) return SPNG_EBKGD_PLTE_IDX;\n            }\n\n            ctx->file.bkgd = 1;\n            ctx->stored.bkgd = 1;\n        }\n        else if(!memcmp(chunk.type, type_trns, 4))\n        {\n            if(ctx->state == SPNG_STATE_AFTER_IDAT) return SPNG_ECHUNK_POS;\n            if(ctx->file.trns) return SPNG_EDUP_TRNS;\n            if(!chunk.length) return SPNG_ECHUNK_SIZE;\n\n            if(ctx->ihdr.color_type == 0)\n            {\n                if(chunk.length != 2) return SPNG_ECHUNK_SIZE;\n\n                ctx->trns.gray = read_u16(data);\n            }\n            else if(ctx->ihdr.color_type == 2)\n            {\n                if(chunk.length != 6) return SPNG_ECHUNK_SIZE;\n\n                ctx->trns.red = read_u16(data);\n                ctx->trns.green = read_u16(data + 2);\n                ctx->trns.blue = read_u16(data + 4);\n            }\n            else if(ctx->ihdr.color_type == 3)\n            {\n                if(chunk.length > ctx->plte.n_entries) return SPNG_ECHUNK_SIZE;\n                if(!ctx->file.plte) return SPNG_ETRNS_NO_PLTE;\n\n                memcpy(ctx->trns.type3_alpha, data, chunk.length);\n                ctx->trns.n_type3_entries = chunk.length;\n            }\n\n            if(ctx->ihdr.color_type == 4 || ctx->ihdr.color_type == 6)  return SPNG_ETRNS_COLOR_TYPE;\n\n            ctx->file.trns = 1;\n            ctx->stored.trns = 1;\n        }\n        else if(!memcmp(chunk.type, type_hist, 4))\n        {\n            if(!ctx->file.plte) return SPNG_EHIST_NO_PLTE;\n            if(ctx->state == SPNG_STATE_AFTER_IDAT) return SPNG_ECHUNK_POS;\n            if(ctx->file.hist) return SPNG_EDUP_HIST;\n\n            if( (chunk.length / 2) != (ctx->plte.n_entries) ) return SPNG_ECHUNK_SIZE;\n\n            size_t k;\n            for(k=0; k < (chunk.length / 2); k++)\n            {\n                ctx->hist.frequency[k] = read_u16(data + k*2);\n            }\n\n            ctx->file.hist = 1;\n            ctx->stored.hist = 1;\n        }\n        else if(!memcmp(chunk.type, type_phys, 4))\n        {\n            if(ctx->state == SPNG_STATE_AFTER_IDAT) return SPNG_ECHUNK_POS;\n            if(ctx->file.phys) return SPNG_EDUP_PHYS;\n\n            if(chunk.length != 9) return SPNG_ECHUNK_SIZE;\n\n            ctx->phys.ppu_x = read_u32(data);\n            ctx->phys.ppu_y = read_u32(data + 4);\n            ctx->phys.unit_specifier = data[8];\n\n            if(check_phys(&ctx->phys)) return SPNG_EPHYS;\n\n            ctx->file.phys = 1;\n            ctx->stored.phys = 1;\n        }\n        else if(!memcmp(chunk.type, type_time, 4))\n        {\n            if(ctx->file.time) return SPNG_EDUP_TIME;\n\n            if(chunk.length != 7) return SPNG_ECHUNK_SIZE;\n\n            struct spng_time time;\n\n            time.year = read_u16(data);\n            time.month = data[2];\n            time.day = data[3];\n            time.hour = data[4];\n            time.minute = data[5];\n            time.second = data[6];\n\n            if(check_time(&time)) return SPNG_ETIME;\n\n            ctx->file.time = 1;\n\n            if(!ctx->user.time) ctx->time = time;\n\n            ctx->stored.time = 1;\n        }\n        else if(!memcmp(chunk.type, type_offs, 4))\n        {\n            if(ctx->state == SPNG_STATE_AFTER_IDAT) return SPNG_ECHUNK_POS;\n            if(ctx->file.offs) return SPNG_EDUP_OFFS;\n\n            if(chunk.length != 9) return SPNG_ECHUNK_SIZE;\n\n            ctx->offs.x = read_s32(data);\n            ctx->offs.y = read_s32(data + 4);\n            ctx->offs.unit_specifier = data[8];\n\n            if(check_offs(&ctx->offs)) return SPNG_EOFFS;\n\n            ctx->file.offs = 1;\n            ctx->stored.offs = 1;\n        }\n        else /* Arbitrary-length chunk */\n        {\n\n            if(!memcmp(chunk.type, type_exif, 4))\n            {\n                if(ctx->file.exif) return SPNG_EDUP_EXIF;\n\n                ctx->file.exif = 1;\n\n                if(ctx->user.exif) goto discard;\n\n                if(increase_cache_usage(ctx, chunk.length, 1)) return SPNG_ECHUNK_LIMITS;\n\n                struct spng_exif exif;\n\n                exif.length = chunk.length;\n\n                exif.data = spng__malloc(ctx, chunk.length);\n                if(exif.data == NULL) return SPNG_EMEM;\n\n                ret = read_chunk_bytes2(ctx, exif.data, chunk.length);\n                if(ret)\n                {\n                    spng__free(ctx, exif.data);\n                    return ret;\n                }\n\n                if(check_exif(&exif))\n                {\n                    spng__free(ctx, exif.data);\n                    return SPNG_EEXIF;\n                }\n\n                ctx->exif = exif;\n\n                ctx->stored.exif = 1;\n            }\n            else if(!memcmp(chunk.type, type_iccp, 4))\n            {/* TODO: add test file with color profile */\n                if(ctx->file.plte) return SPNG_ECHUNK_POS;\n                if(ctx->state == SPNG_STATE_AFTER_IDAT) return SPNG_ECHUNK_POS;\n                if(ctx->file.iccp) return SPNG_EDUP_ICCP;\n                if(!chunk.length) return SPNG_ECHUNK_SIZE;\n\n                ctx->file.iccp = 1;\n\n                uint32_t peek_bytes =  81 > chunk.length ? chunk.length : 81;\n\n                ret = read_chunk_bytes(ctx, peek_bytes);\n                if(ret) return ret;\n\n                unsigned char *keyword_nul = memchr(ctx->data, '\\0', peek_bytes);\n                if(keyword_nul == NULL) return SPNG_EICCP_NAME;\n\n                uint32_t keyword_len = keyword_nul - ctx->data;\n\n                if(keyword_len > 79) return SPNG_EICCP_NAME;\n\n                memcpy(ctx->iccp.profile_name, ctx->data, keyword_len);\n\n                if(check_png_keyword(ctx->iccp.profile_name)) return SPNG_EICCP_NAME;\n\n                if(chunk.length < (keyword_len + 2)) return SPNG_ECHUNK_SIZE;\n\n                if(ctx->data[keyword_len + 1] != 0) return SPNG_EICCP_COMPRESSION_METHOD;\n\n                ret = spng__inflate_stream(ctx, &ctx->iccp.profile, &ctx->iccp.profile_len, 0, ctx->data + keyword_len + 2, peek_bytes - (keyword_len + 2));\n\n                if(ret) return ret;\n\n                ctx->stored.iccp = 1;\n            }\n             else if(!memcmp(chunk.type, type_text, 4) ||\n                     !memcmp(chunk.type, type_ztxt, 4) ||\n                     !memcmp(chunk.type, type_itxt, 4))\n            {\n                if(!chunk.length) return SPNG_ECHUNK_SIZE;\n\n                ctx->file.text = 1;\n\n                if(ctx->user.text) goto discard;\n\n                if(increase_cache_usage(ctx, sizeof(struct spng_text2), 1)) return SPNG_ECHUNK_LIMITS;\n\n                ctx->n_text++;\n                if(ctx->n_text < 1) return SPNG_EOVERFLOW;\n                if(sizeof(struct spng_text2) > SIZE_MAX / ctx->n_text) return SPNG_EOVERFLOW;\n\n                void *buf = spng__realloc(ctx, ctx->text_list, ctx->n_text * sizeof(struct spng_text2));\n                if(buf == NULL) return SPNG_EMEM;\n                ctx->text_list = buf;\n\n                struct spng_text2 *text = &ctx->text_list[ctx->n_text - 1];\n                memset(text, 0, sizeof(struct spng_text2));\n\n                ctx->undo = text_undo;\n\n                uint32_t text_offset = 0, language_tag_offset = 0, translated_keyword_offset = 0;\n                uint32_t peek_bytes = 256; /* enough for 3 80-byte keywords and some text bytes */\n                uint32_t keyword_len;\n\n                if(peek_bytes > chunk.length) peek_bytes = chunk.length;\n\n                ret = read_chunk_bytes(ctx, peek_bytes);\n                if(ret) return ret;\n\n                data = ctx->data;\n\n                const unsigned char *zlib_stream = NULL;\n                const unsigned char *peek_end = data + peek_bytes;\n                const unsigned char *keyword_nul = memchr(data, 0, chunk.length > 80 ? 80 : chunk.length);\n\n                if(keyword_nul == NULL) return SPNG_ETEXT_KEYWORD;\n\n                keyword_len = keyword_nul - data;\n\n                if(!memcmp(chunk.type, type_text, 4))\n                {\n                    text->type = SPNG_TEXT;\n\n                    text->text_length = chunk.length - keyword_len - 1;\n\n                    text_offset = keyword_len;\n\n                    /* increment past nul if there is a text field */\n                    if(text->text_length) text_offset++;\n                }\n                else if(!memcmp(chunk.type, type_ztxt, 4))\n                {\n                    text->type = SPNG_ZTXT;\n\n                    if((peek_bytes - keyword_len) <= 2) return SPNG_EZTXT;\n\n                    if(keyword_nul[1]) return SPNG_EZTXT_COMPRESSION_METHOD;\n\n                    text->compression_flag = 1;\n\n                    text_offset = keyword_len + 2;\n                }\n                else if(!memcmp(chunk.type, type_itxt, 4))\n                {\n                    text->type = SPNG_ITXT;\n\n                    /* at least two 1-byte fields, two >=0 length strings, and one byte of (compressed) text */\n                    if((peek_bytes - keyword_len) < 5) return SPNG_EITXT;\n\n                    text->compression_flag = keyword_nul[1];\n\n                    if(text->compression_flag > 1) return SPNG_EITXT_COMPRESSION_FLAG;\n\n                    if(keyword_nul[2]) return SPNG_EITXT_COMPRESSION_METHOD;\n\n                    language_tag_offset = keyword_len + 3;\n\n                    const unsigned char *term;\n                    term = memchr(data + language_tag_offset, 0, peek_bytes - language_tag_offset);\n                    if(term == NULL) return SPNG_EITXT_LANG_TAG;\n\n                    if((peek_end - term) < 2) return SPNG_EITXT;\n\n                    translated_keyword_offset = term - data + 1;\n\n                    zlib_stream = memchr(data + translated_keyword_offset, 0, peek_bytes - translated_keyword_offset);\n                    if(zlib_stream == NULL) return SPNG_EITXT;\n                    if(zlib_stream == peek_end) return SPNG_EITXT;\n\n                    text_offset = zlib_stream - data + 1;\n                    text->text_length = chunk.length - text_offset;\n                }\n                else return SPNG_EINTERNAL;\n\n\n                if(text->compression_flag)\n                {\n                    /* cache usage = peek_bytes + decompressed text size + nul */\n                    if(increase_cache_usage(ctx, peek_bytes, 0)) return SPNG_ECHUNK_LIMITS;\n\n                    text->keyword = spng__calloc(ctx, 1, peek_bytes);\n                    if(text->keyword == NULL) return SPNG_EMEM;\n\n                    memcpy(text->keyword, data, peek_bytes);\n\n                    zlib_stream = ctx->data + text_offset;\n\n                    ret = spng__inflate_stream(ctx, &text->text, &text->text_length, 1, zlib_stream, peek_bytes - text_offset);\n\n                    if(ret) return ret;\n\n                    text->text[text->text_length - 1] = '\\0';\n                    text->cache_usage = text->text_length + peek_bytes;\n                }\n                else\n                {\n                    if(increase_cache_usage(ctx, chunk.length + 1, 0)) return SPNG_ECHUNK_LIMITS;\n\n                    text->keyword = spng__malloc(ctx, chunk.length + 1);\n                    if(text->keyword == NULL) return SPNG_EMEM;\n\n                    memcpy(text->keyword, data, peek_bytes);\n\n                    if(chunk.length > peek_bytes)\n                    {\n                        ret = read_chunk_bytes2(ctx, text->keyword + peek_bytes, chunk.length - peek_bytes);\n                        if(ret) return ret;\n                    }\n\n                    text->text = text->keyword + text_offset;\n\n                    text->text_length = chunk.length - text_offset;\n\n                    text->text[text->text_length] = '\\0';\n                    text->cache_usage = chunk.length + 1;\n                }\n\n                if(check_png_keyword(text->keyword)) return SPNG_ETEXT_KEYWORD;\n\n                text->text_length = strlen(text->text);\n\n                if(text->type != SPNG_ITXT)\n                {\n                    language_tag_offset = keyword_len;\n                    translated_keyword_offset = keyword_len;\n\n                    if(ctx->strict && check_png_text(text->text, text->text_length))\n                    {\n                        if(text->type == SPNG_ZTXT) return SPNG_EZTXT;\n                        else return SPNG_ETEXT;\n                    }\n                }\n\n                text->language_tag = text->keyword + language_tag_offset;\n                text->translated_keyword = text->keyword + translated_keyword_offset;\n\n                ctx->stored.text = 1;\n            }\n            else if(!memcmp(chunk.type, type_splt, 4))\n            {\n                if(ctx->state == SPNG_STATE_AFTER_IDAT) return SPNG_ECHUNK_POS;\n                if(ctx->user.splt) goto discard; /* XXX: could check profile names for uniqueness */\n                if(!chunk.length) return SPNG_ECHUNK_SIZE;\n\n                ctx->file.splt = 1;\n\n                /* chunk.length + sizeof(struct spng_splt) + splt->n_entries * sizeof(struct spng_splt_entry) */\n                if(increase_cache_usage(ctx, chunk.length + sizeof(struct spng_splt), 1)) return SPNG_ECHUNK_LIMITS;\n\n                ctx->n_splt++;\n                if(ctx->n_splt < 1) return SPNG_EOVERFLOW;\n                if(sizeof(struct spng_splt) > SIZE_MAX / ctx->n_splt) return SPNG_EOVERFLOW;\n\n                void *buf = spng__realloc(ctx, ctx->splt_list, ctx->n_splt * sizeof(struct spng_splt));\n                if(buf == NULL) return SPNG_EMEM;\n                ctx->splt_list = buf;\n\n                struct spng_splt *splt = &ctx->splt_list[ctx->n_splt - 1];\n\n                memset(splt, 0, sizeof(struct spng_splt));\n\n                ctx->undo = splt_undo;\n\n                void *t = spng__malloc(ctx, chunk.length);\n                if(t == NULL) return SPNG_EMEM;\n\n                splt->entries = t; /* simplifies error handling */\n                data = t;\n\n                ret = read_chunk_bytes2(ctx, t, chunk.length);\n                if(ret) return ret;\n\n                uint32_t keyword_len = chunk.length < 80 ? chunk.length : 80;\n\n                const unsigned char *keyword_nul = memchr(data, 0, keyword_len);\n                if(keyword_nul == NULL) return SPNG_ESPLT_NAME;\n\n                keyword_len = keyword_nul - data;\n\n                memcpy(splt->name, data, keyword_len);\n\n                if(check_png_keyword(splt->name)) return SPNG_ESPLT_NAME;\n\n                uint32_t j;\n                for(j=0; j < (ctx->n_splt - 1); j++)\n                {\n                    if(!strcmp(ctx->splt_list[j].name, splt->name)) return SPNG_ESPLT_DUP_NAME;\n                }\n\n                if( (chunk.length - keyword_len) <= 2) return SPNG_ECHUNK_SIZE;\n\n                splt->sample_depth = data[keyword_len + 1];\n\n                uint32_t entries_len = chunk.length - keyword_len - 2;\n                if(!entries_len) return SPNG_ECHUNK_SIZE;\n\n                if(splt->sample_depth == 16)\n                {\n                    if(entries_len % 10 != 0) return SPNG_ECHUNK_SIZE;\n                    splt->n_entries = entries_len / 10;\n                }\n                else if(splt->sample_depth == 8)\n                {\n                    if(entries_len % 6 != 0) return SPNG_ECHUNK_SIZE;\n                    splt->n_entries = entries_len / 6;\n                }\n                else return SPNG_ESPLT_DEPTH;\n\n                if(!splt->n_entries) return SPNG_ECHUNK_SIZE;\n\n                size_t list_size = splt->n_entries;\n\n                if(list_size > SIZE_MAX / sizeof(struct spng_splt_entry)) return SPNG_EOVERFLOW;\n\n                list_size *= sizeof(struct spng_splt_entry);\n\n                if(increase_cache_usage(ctx, list_size, 0)) return SPNG_ECHUNK_LIMITS;\n\n                splt->entries = spng__malloc(ctx, list_size);\n                if(splt->entries == NULL)\n                {\n                    spng__free(ctx, t);\n                    return SPNG_EMEM;\n                }\n\n                data = (unsigned char*)t + keyword_len + 2;\n\n                uint32_t k;\n                if(splt->sample_depth == 16)\n                {\n                    for(k=0; k < splt->n_entries; k++)\n                    {\n                        splt->entries[k].red =   read_u16(data + k * 10);\n                        splt->entries[k].green = read_u16(data + k * 10 + 2);\n                        splt->entries[k].blue =  read_u16(data + k * 10 + 4);\n                        splt->entries[k].alpha = read_u16(data + k * 10 + 6);\n                        splt->entries[k].frequency = read_u16(data + k * 10 + 8);\n                    }\n                }\n                else if(splt->sample_depth == 8)\n                {\n                    for(k=0; k < splt->n_entries; k++)\n                    {\n                        splt->entries[k].red =   data[k * 6];\n                        splt->entries[k].green = data[k * 6 + 1];\n                        splt->entries[k].blue =  data[k * 6 + 2];\n                        splt->entries[k].alpha = data[k * 6 + 3];\n                        splt->entries[k].frequency = read_u16(data + k * 6 + 4);\n                    }\n                }\n\n                spng__free(ctx, t);\n                decrease_cache_usage(ctx, chunk.length);\n\n                ctx->stored.splt = 1;\n            }\n            else /* Unknown chunk */\n            {\n                ctx->file.unknown = 1;\n\n                if(!ctx->keep_unknown) goto discard;\n                if(ctx->user.unknown) goto discard;\n\n                if(increase_cache_usage(ctx, chunk.length + sizeof(struct spng_unknown_chunk), 1)) return SPNG_ECHUNK_LIMITS;\n\n                ctx->n_chunks++;\n                if(ctx->n_chunks < 1) return SPNG_EOVERFLOW;\n                if(sizeof(struct spng_unknown_chunk) > SIZE_MAX / ctx->n_chunks) return SPNG_EOVERFLOW;\n\n                void *buf = spng__realloc(ctx, ctx->chunk_list, ctx->n_chunks * sizeof(struct spng_unknown_chunk));\n                if(buf == NULL) return SPNG_EMEM;\n                ctx->chunk_list = buf;\n\n                struct spng_unknown_chunk *chunkp = &ctx->chunk_list[ctx->n_chunks - 1];\n\n                memset(chunkp, 0, sizeof(struct spng_unknown_chunk));\n\n                ctx->undo = chunk_undo;\n\n                memcpy(chunkp->type, chunk.type, 4);\n\n                if(ctx->state < SPNG_STATE_FIRST_IDAT)\n                {\n                    if(ctx->file.plte) chunkp->location = SPNG_AFTER_PLTE;\n                    else chunkp->location = SPNG_AFTER_IHDR;\n                }\n                else if(ctx->state >= SPNG_STATE_AFTER_IDAT) chunkp->location = SPNG_AFTER_IDAT;\n\n                if(chunk.length > 0)\n                {\n                    void *t = spng__malloc(ctx, chunk.length);\n                    if(t == NULL) return SPNG_EMEM;\n\n                    ret = read_chunk_bytes2(ctx, t, chunk.length);\n                    if(ret)\n                    {\n                        spng__free(ctx, t);\n                        return ret;\n                    }\n\n                    chunkp->length = chunk.length;\n                    chunkp->data = t;\n                }\n\n                ctx->stored.unknown = 1;\n            }\n\ndiscard:\n            ret = discard_chunk_bytes(ctx, ctx->cur_chunk_bytes_left);\n            if(ret) return ret;\n        }\n\n    }\n\n    return ret;\n}\n\n/* Read chunks before or after the IDAT chunks depending on state */\nstatic int read_chunks(spng_ctx *ctx, int only_ihdr)\n{\n    if(ctx == NULL) return SPNG_EINTERNAL;\n    if(!ctx->state) return SPNG_EBADSTATE;\n    if(ctx->data == NULL)\n    {\n        if(ctx->encode_only) return 0;\n        else return SPNG_EINTERNAL;\n    }\n\n    int ret = 0;\n\n    if(ctx->state == SPNG_STATE_INPUT)\n    {\n        ret = read_ihdr(ctx);\n\n        if(ret) return decode_err(ctx, ret);\n\n        ctx->state = SPNG_STATE_IHDR;\n    }\n\n    if(only_ihdr) return 0;\n\n    if(ctx->state == SPNG_STATE_EOI)\n    {\n        ctx->state = SPNG_STATE_AFTER_IDAT;\n        ctx->prev_was_idat = 1;\n    }\n\n    while(ctx->state < SPNG_STATE_FIRST_IDAT || ctx->state == SPNG_STATE_AFTER_IDAT)\n    {\n        ret = read_non_idat_chunks(ctx);\n\n        if(!ret)\n        {\n            if(ctx->state < SPNG_STATE_FIRST_IDAT) ctx->state = SPNG_STATE_FIRST_IDAT;\n            else if(ctx->state == SPNG_STATE_AFTER_IDAT) ctx->state = SPNG_STATE_IEND;\n        }\n        else\n        {\n            switch(ret)\n            {\n                case SPNG_ECHUNK_POS:\n                case SPNG_ECHUNK_SIZE: /* size != expected size, SPNG_ECHUNK_STDLEN = invalid size */\n                case SPNG_EDUP_PLTE:\n                case SPNG_EDUP_CHRM:\n                case SPNG_EDUP_GAMA:\n                case SPNG_EDUP_ICCP:\n                case SPNG_EDUP_SBIT:\n                case SPNG_EDUP_SRGB:\n                case SPNG_EDUP_BKGD:\n                case SPNG_EDUP_HIST:\n                case SPNG_EDUP_TRNS:\n                case SPNG_EDUP_PHYS:\n                case SPNG_EDUP_TIME:\n                case SPNG_EDUP_OFFS:\n                case SPNG_EDUP_EXIF:\n                case SPNG_ECHRM:\n                case SPNG_ETRNS_COLOR_TYPE:\n                case SPNG_ETRNS_NO_PLTE:\n                case SPNG_EGAMA:\n                case SPNG_EICCP_NAME:\n                case SPNG_EICCP_COMPRESSION_METHOD:\n                case SPNG_ESBIT:\n                case SPNG_ESRGB:\n                case SPNG_ETEXT:\n                case SPNG_ETEXT_KEYWORD:\n                case SPNG_EZTXT:\n                case SPNG_EZTXT_COMPRESSION_METHOD:\n                case SPNG_EITXT:\n                case SPNG_EITXT_COMPRESSION_FLAG:\n                case SPNG_EITXT_COMPRESSION_METHOD:\n                case SPNG_EITXT_LANG_TAG:\n                case SPNG_EITXT_TRANSLATED_KEY:\n                case SPNG_EBKGD_NO_PLTE:\n                case SPNG_EBKGD_PLTE_IDX:\n                case SPNG_EHIST_NO_PLTE:\n                case SPNG_EPHYS:\n                case SPNG_ESPLT_NAME:\n                case SPNG_ESPLT_DUP_NAME:\n                case SPNG_ESPLT_DEPTH:\n                case SPNG_ETIME:\n                case SPNG_EOFFS:\n                case SPNG_EEXIF:\n                case SPNG_EZLIB:\n                {\n                    if(!ctx->strict && !is_critical_chunk(&ctx->current_chunk))\n                    {\n                        ret = discard_chunk_bytes(ctx, ctx->cur_chunk_bytes_left);\n                        if(ret) return decode_err(ctx, ret);\n\n                        if(ctx->undo) ctx->undo(ctx);\n\n                        ctx->stored = ctx->prev_stored;\n\n                        ctx->discard = 0;\n                        ctx->undo = NULL;\n\n                        continue;\n                    }\n                    else return decode_err(ctx, ret);\n\n                    break;\n                }\n                default: return decode_err(ctx, ret);\n            }\n        }\n    }\n\n    return ret;\n}\n\nstatic int read_scanline(spng_ctx *ctx)\n{\n    int ret, pass = ctx->row_info.pass;\n    struct spng_row_info *ri = &ctx->row_info;\n    const struct spng_subimage *sub = ctx->subimage;\n    size_t scanline_width = sub[pass].scanline_width;\n    uint32_t scanline_idx = ri->scanline_idx;\n\n    uint8_t next_filter = 0;\n\n    if(scanline_idx == (sub[pass].height - 1) && ri->pass == ctx->last_pass)\n    {\n        ret = read_scanline_bytes(ctx, ctx->scanline, scanline_width - 1);\n    }\n    else\n    {\n        ret = read_scanline_bytes(ctx, ctx->scanline, scanline_width);\n        if(ret) return ret;\n\n        next_filter = ctx->scanline[scanline_width - 1];\n        if(next_filter > 4) ret = SPNG_EFILTER;\n    }\n\n    if(ret) return ret;\n\n    if(!scanline_idx && ri->filter > 1)\n    {\n        /* prev_scanline is all zeros for the first scanline */\n        memset(ctx->prev_scanline, 0, scanline_width);\n    }\n\n    if(ctx->ihdr.bit_depth == 16 && ctx->fmt != SPNG_FMT_RAW) u16_row_to_host(ctx->scanline, scanline_width - 1);\n\n    ret = defilter_scanline(ctx->prev_scanline, ctx->scanline, scanline_width, ctx->bytes_per_pixel, ri->filter);\n    if(ret) return ret;\n\n    ri->filter = next_filter;\n\n    return 0;\n}\n\nstatic int update_row_info(spng_ctx *ctx)\n{\n    int interlacing = ctx->ihdr.interlace_method;\n    struct spng_row_info *ri = &ctx->row_info;\n    const struct spng_subimage *sub = ctx->subimage;\n\n    if(ri->scanline_idx == (sub[ri->pass].height - 1)) /* Last scanline */\n    {\n        if(ri->pass == ctx->last_pass)\n        {\n            ctx->state = SPNG_STATE_EOI;\n\n            return SPNG_EOI;\n        }\n\n        ri->scanline_idx = 0;\n        ri->pass++;\n\n        /* Skip empty passes */\n        while( (!sub[ri->pass].width || !sub[ri->pass].height) && (ri->pass < ctx->last_pass)) ri->pass++;\n    }\n    else\n    {\n        ri->row_num++;\n        ri->scanline_idx++;\n    }\n\n    if(interlacing) ri->row_num = adam7_y_start[ri->pass] + ri->scanline_idx * adam7_y_delta[ri->pass];\n\n    return 0;\n}\n\nint spng_decode_scanline(spng_ctx *ctx, void *out, size_t len)\n{\n    if(ctx == NULL || out == NULL) return 1;\n\n    if(ctx->state >= SPNG_STATE_EOI) return SPNG_EOI;\n\n    struct decode_flags f = ctx->decode_flags;\n\n    struct spng_row_info *ri = &ctx->row_info;\n    const struct spng_subimage *sub = ctx->subimage;\n\n    const struct spng_ihdr *ihdr = &ctx->ihdr;\n    const uint16_t *gamma_lut = ctx->gamma_lut;\n    unsigned char *trns_px = ctx->trns_px;\n    const struct spng_sbit *sb = &ctx->decode_sb;\n    const struct spng_plte_entry *plte = ctx->decode_plte.rgba;\n    struct spng__iter iter = spng__iter_init(ihdr->bit_depth, ctx->scanline);\n\n    const unsigned char *scanline;\n\n    const int pass = ri->pass;\n    const int fmt = ctx->fmt;\n    const size_t scanline_width = sub[pass].scanline_width;\n    const uint32_t width = sub[pass].width;\n    uint32_t k;\n    uint8_t r_8, g_8, b_8, a_8, gray_8;\n    uint16_t r_16, g_16, b_16, a_16, gray_16;\n    r_8=0; g_8=0; b_8=0; a_8=0; gray_8=0;\n    r_16=0; g_16=0; b_16=0; a_16=0; gray_16=0;\n    size_t pixel_size = 4; /* SPNG_FMT_RGBA8 */\n    size_t pixel_offset = 0;\n    unsigned char *pixel;\n    unsigned processing_depth = ihdr->bit_depth;\n\n    if(f.indexed) processing_depth = 8;\n\n    if(fmt == SPNG_FMT_RGBA16) pixel_size = 8;\n    else if(fmt == SPNG_FMT_RGB8) pixel_size = 3;\n\n    if(len < sub[pass].out_width) return SPNG_EBUFSIZ;\n\n    int ret = read_scanline(ctx);\n\n    if(ret) return decode_err(ctx, ret);\n\n    scanline = ctx->scanline;\n\n    for(k=0; k < width; k++)\n    {\n        pixel = (unsigned char*)out + pixel_offset;\n        pixel_offset += pixel_size;\n\n        if(f.same_layout)\n        {\n            if(f.zerocopy) break;\n\n            memcpy(out, scanline, scanline_width - 1);\n            break;\n        }\n\n        if(f.unpack)\n        {\n            unpack_scanline(out, scanline, width, ihdr->bit_depth, fmt);\n            break;\n        }\n\n        if(ihdr->color_type == SPNG_COLOR_TYPE_TRUECOLOR)\n        {\n            if(ihdr->bit_depth == 16)\n            {\n                memcpy(&r_16, scanline + (k * 6), 2);\n                memcpy(&g_16, scanline + (k * 6) + 2, 2);\n                memcpy(&b_16, scanline + (k * 6) + 4, 2);\n\n                a_16 = 65535;\n            }\n            else /* == 8 */\n            {\n                if(fmt == SPNG_FMT_RGBA8)\n                {\n                    rgb8_row_to_rgba8(scanline, out, width);\n                    break;\n                }\n\n                r_8 = scanline[k * 3];\n                g_8 = scanline[k * 3 + 1];\n                b_8 = scanline[k * 3 + 2];\n\n                a_8 = 255;\n            }\n        }\n        else if(ihdr->color_type == SPNG_COLOR_TYPE_INDEXED)\n        {\n            uint8_t entry = 0;\n\n            if(ihdr->bit_depth == 8)\n            {\n                if(fmt & (SPNG_FMT_RGBA8 | SPNG_FMT_RGB8))\n                {\n                    expand_row(out, scanline, &ctx->decode_plte, width, fmt);\n                    break;\n                }\n\n                entry = scanline[k];\n            }\n            else /* < 8 */\n            {\n                entry = get_sample(&iter);\n            }\n\n            if(fmt & (SPNG_FMT_RGBA8 | SPNG_FMT_RGB8))\n            {\n                pixel[0] = plte[entry].red;\n                pixel[1] = plte[entry].green;\n                pixel[2] = plte[entry].blue;\n                if(fmt == SPNG_FMT_RGBA8) pixel[3] = plte[entry].alpha;\n\n                continue;\n            }\n            else /* RGBA16 */\n            {\n                r_16 = plte[entry].red;\n                g_16 = plte[entry].green;\n                b_16 = plte[entry].blue;\n                a_16 = plte[entry].alpha;\n\n                r_16 = (r_16 << 8) | r_16;\n                g_16 = (g_16 << 8) | g_16;\n                b_16 = (b_16 << 8) | b_16;\n                a_16 = (a_16 << 8) | a_16;\n\n                memcpy(pixel, &r_16, 2);\n                memcpy(pixel + 2, &g_16, 2);\n                memcpy(pixel + 4, &b_16, 2);\n                memcpy(pixel + 6, &a_16, 2);\n\n                continue;\n            }\n        }\n        else if(ihdr->color_type == SPNG_COLOR_TYPE_TRUECOLOR_ALPHA)\n        {\n            if(ihdr->bit_depth == 16)\n            {\n                memcpy(&r_16, scanline + (k * 8), 2);\n                memcpy(&g_16, scanline + (k * 8) + 2, 2);\n                memcpy(&b_16, scanline + (k * 8) + 4, 2);\n                memcpy(&a_16, scanline + (k * 8) + 6, 2);\n            }\n            else /* == 8 */\n            {\n                r_8 = scanline[k * 4];\n                g_8 = scanline[k * 4 + 1];\n                b_8 = scanline[k * 4 + 2];\n                a_8 = scanline[k * 4 + 3];\n            }\n        }\n        else if(ihdr->color_type == SPNG_COLOR_TYPE_GRAYSCALE)\n        {\n            if(ihdr->bit_depth == 16)\n            {\n                memcpy(&gray_16, scanline + k * 2, 2);\n\n                if(f.apply_trns && ctx->trns.gray == gray_16) a_16 = 0;\n                else a_16 = 65535;\n\n                r_16 = gray_16;\n                g_16 = gray_16;\n                b_16 = gray_16;\n            }\n            else /* <= 8 */\n            {\n                gray_8 = get_sample(&iter);\n\n                if(f.apply_trns && ctx->trns.gray == gray_8) a_8 = 0;\n                else a_8 = 255;\n\n                r_8 = gray_8; g_8 = gray_8; b_8 = gray_8;\n            }\n        }\n        else if(ihdr->color_type == SPNG_COLOR_TYPE_GRAYSCALE_ALPHA)\n        {\n            if(ihdr->bit_depth == 16)\n            {\n                memcpy(&gray_16, scanline + (k * 4), 2);\n                memcpy(&a_16, scanline + (k * 4) + 2, 2);\n\n                r_16 = gray_16;\n                g_16 = gray_16;\n                b_16 = gray_16;\n            }\n            else /* == 8 */\n            {\n                gray_8 = scanline[k * 2];\n                a_8 = scanline[k * 2 + 1];\n\n                r_8 = gray_8;\n                g_8 = gray_8;\n                b_8 = gray_8;\n            }\n        }\n\n\n        if(fmt & (SPNG_FMT_RGBA8 | SPNG_FMT_RGB8))\n        {\n            if(ihdr->bit_depth == 16)\n            {\n                r_8 = r_16 >> 8;\n                g_8 = g_16 >> 8;\n                b_8 = b_16 >> 8;\n                a_8 = a_16 >> 8;\n            }\n\n            pixel[0] = r_8;\n            pixel[1] = g_8;\n            pixel[2] = b_8;\n\n            if(fmt == SPNG_FMT_RGBA8) pixel[3] = a_8;\n        }\n        else if(fmt == SPNG_FMT_RGBA16)\n        {\n            if(ihdr->bit_depth != 16)\n            {\n                r_16 = r_8;\n                g_16 = g_8;\n                b_16 = b_8;\n                a_16 = a_8;\n            }\n\n            memcpy(pixel, &r_16, 2);\n            memcpy(pixel + 2, &g_16, 2);\n            memcpy(pixel + 4, &b_16, 2);\n            memcpy(pixel + 6, &a_16, 2);\n        }\n    }/* for(k=0; k < width; k++) */\n\n    if(f.apply_trns) trns_row(out, scanline, trns_px, ctx->bytes_per_pixel, &ctx->ihdr, width, fmt);\n\n    if(f.do_scaling) scale_row(out, width, fmt, processing_depth, sb);\n\n    if(f.apply_gamma) gamma_correct_row(out, width, fmt, gamma_lut);\n\n    /* The previous scanline is always defiltered */\n    void *t = ctx->prev_scanline;\n    ctx->prev_scanline = ctx->scanline;\n    ctx->scanline = t;\n\n    ret = update_row_info(ctx);\n\n    if(ret == SPNG_EOI)\n    {\n        if(ctx->cur_chunk_bytes_left) /* zlib stream ended before an IDAT chunk boundary */\n        {/* Discard the rest of the chunk */\n            int error = discard_chunk_bytes(ctx, ctx->cur_chunk_bytes_left);\n            if(error) return decode_err(ctx, error);\n        }\n\n        ctx->last_idat = ctx->current_chunk;\n    }\n\n    return ret;\n}\n\nint spng_decode_row(spng_ctx *ctx, void *out, size_t len)\n{\n    if(ctx == NULL || out == NULL) return 1;\n    if(ctx->state >= SPNG_STATE_EOI) return SPNG_EOI;\n    if(len < ctx->image_width) return SPNG_EBUFSIZ;\n\n    const struct spng_ihdr *ihdr = &ctx->ihdr;\n    int ret, pass = ctx->row_info.pass;\n    unsigned char *outptr = out;\n\n    if(!ihdr->interlace_method || pass == 6) return spng_decode_scanline(ctx, out, len);\n\n    ret = spng_decode_scanline(ctx, ctx->row, ctx->image_width);\n    if(ret && ret != SPNG_EOI) return ret;\n\n    uint32_t k;\n    unsigned pixel_size = 4; /* RGBA8 */\n    if(ctx->fmt == SPNG_FMT_RGBA16) pixel_size = 8;\n    else if(ctx->fmt == SPNG_FMT_RGB8) pixel_size = 3;\n    else if(ctx->fmt == SPNG_FMT_G8) pixel_size = 1;\n    else if(ctx->fmt == SPNG_FMT_GA8) pixel_size = 2;\n    else if(ctx->fmt & (SPNG_FMT_PNG | SPNG_FMT_RAW))\n    {\n        if(ihdr->bit_depth < 8)\n        {\n            struct spng__iter iter = spng__iter_init(ihdr->bit_depth, ctx->row);\n            const uint8_t samples_per_byte = 8 / ihdr->bit_depth;\n            uint8_t sample;\n\n            for(k=0; k < ctx->subimage[pass].width; k++)\n            {\n                sample = get_sample(&iter);\n\n                size_t ioffset = adam7_x_start[pass] + k * adam7_x_delta[pass];\n\n                sample = sample << (iter.initial_shift - ioffset * ihdr->bit_depth % 8);\n\n                ioffset /= samples_per_byte;\n\n                outptr[ioffset] |= sample;\n            }\n\n            return 0;\n        }\n        else pixel_size = ctx->bytes_per_pixel;\n    }\n\n    for(k=0; k < ctx->subimage[pass].width; k++)\n    {\n        size_t ioffset = (adam7_x_start[pass] + (size_t) k * adam7_x_delta[pass]) * pixel_size;\n\n        memcpy(outptr + ioffset, ctx->row + k * pixel_size, pixel_size);\n    }\n\n    return 0;\n}\n\nint spng_decode_chunks(spng_ctx *ctx)\n{\n    if(ctx == NULL) return 1;\n    if(ctx->encode_only) return SPNG_ECTXTYPE;\n    if(ctx->state < SPNG_STATE_INPUT) return SPNG_ENOSRC;\n    if(ctx->state == SPNG_STATE_IEND) return 0;\n\n    return read_chunks(ctx, 0);\n}\n\nint spng_decode_image(spng_ctx *ctx, void *out, size_t len, int fmt, int flags)\n{\n    if(ctx == NULL) return 1;\n    if(ctx->encode_only) return SPNG_ECTXTYPE;\n    if(ctx->state >= SPNG_STATE_EOI) return SPNG_EOI;\n\n    const struct spng_ihdr *ihdr = &ctx->ihdr;\n\n    int ret = read_chunks(ctx, 0);\n    if(ret) return decode_err(ctx, ret);\n\n    ret = check_decode_fmt(ihdr, fmt);\n    if(ret) return ret;\n\n    ret = calculate_image_width(ihdr, fmt, &ctx->image_width);\n    if(ret) return decode_err(ctx, ret);\n\n    if(ctx->image_width > SIZE_MAX / ihdr->height) ctx->image_size = 0; /* overflow */\n    else ctx->image_size = ctx->image_width * ihdr->height;\n\n    if( !(flags & SPNG_DECODE_PROGRESSIVE) )\n    {\n        if(out == NULL) return 1;\n        if(!ctx->image_size) return SPNG_EOVERFLOW;\n        if(len < ctx->image_size) return SPNG_EBUFSIZ;\n    }\n\n    uint32_t bytes_read = 0;\n\n    ret = read_idat_bytes(ctx, &bytes_read);\n    if(ret) return decode_err(ctx, ret);\n\n    if(bytes_read > 1)\n    {\n        int valid = read_u16(ctx->data) % 31 ? 0 : 1;\n\n        unsigned flg = ctx->data[1];\n        unsigned flevel = flg >> 6;\n        int compression_level = Z_DEFAULT_COMPRESSION;\n\n        if(flevel == 0) compression_level = 0; /* fastest */\n        else if(flevel == 1) compression_level = 1; /* fast */\n        else if(flevel == 2) compression_level = 6; /* default */\n        else if(flevel == 3) compression_level = 9; /* slowest, max compression */\n\n        if(valid) ctx->image_options.compression_level = compression_level;\n    }\n\n    ret = spng__inflate_init(ctx, ctx->image_options.window_bits);\n    if(ret) return decode_err(ctx, ret);\n\n    ctx->zstream.avail_in = bytes_read;\n    ctx->zstream.next_in = ctx->data;\n\n    size_t scanline_buf_size = ctx->subimage[ctx->widest_pass].scanline_width;\n\n    scanline_buf_size += 32;\n\n    if(scanline_buf_size < 32) return SPNG_EOVERFLOW;\n\n    ctx->scanline_buf = spng__malloc(ctx, scanline_buf_size);\n    ctx->prev_scanline_buf = spng__malloc(ctx, scanline_buf_size);\n\n    ctx->scanline = ctx->scanline_buf;\n    ctx->prev_scanline = ctx->prev_scanline_buf;\n\n    struct decode_flags f = {0};\n\n    ctx->fmt = fmt;\n\n    if(ihdr->color_type == SPNG_COLOR_TYPE_INDEXED) f.indexed = 1;\n\n    unsigned processing_depth = ihdr->bit_depth;\n\n    if(f.indexed) processing_depth = 8;\n\n    if(ihdr->interlace_method)\n    {\n        f.interlaced = 1;\n        ctx->row_buf = spng__malloc(ctx, ctx->image_width);\n        ctx->row = ctx->row_buf;\n\n        if(ctx->row == NULL) return decode_err(ctx, SPNG_EMEM);\n    }\n\n    if(ctx->scanline == NULL || ctx->prev_scanline == NULL)\n    {\n        return decode_err(ctx, SPNG_EMEM);\n    }\n\n    f.do_scaling = 1;\n    if(f.indexed) f.do_scaling = 0;\n\n    unsigned depth_target = 8; /* FMT_RGBA8, G8 */\n    if(fmt == SPNG_FMT_RGBA16) depth_target = 16;\n\n    if(flags & SPNG_DECODE_TRNS && ctx->stored.trns) f.apply_trns = 1;\n    else flags &= ~SPNG_DECODE_TRNS;\n\n    if(ihdr->color_type == SPNG_COLOR_TYPE_GRAYSCALE_ALPHA ||\n       ihdr->color_type == SPNG_COLOR_TYPE_TRUECOLOR_ALPHA) flags &= ~SPNG_DECODE_TRNS;\n\n    if(flags & SPNG_DECODE_GAMMA && ctx->stored.gama) f.apply_gamma = 1;\n    else flags &= ~SPNG_DECODE_GAMMA;\n\n    if(flags & SPNG_DECODE_USE_SBIT && ctx->stored.sbit) f.use_sbit = 1;\n    else flags &= ~SPNG_DECODE_USE_SBIT;\n\n    if(fmt & (SPNG_FMT_RGBA8 | SPNG_FMT_RGBA16))\n    {\n        if(ihdr->color_type == SPNG_COLOR_TYPE_TRUECOLOR_ALPHA &&\n           ihdr->bit_depth == depth_target) f.same_layout = 1;\n    }\n    else if(fmt == SPNG_FMT_RGB8)\n    {\n        if(ihdr->color_type == SPNG_COLOR_TYPE_TRUECOLOR &&\n           ihdr->bit_depth == depth_target) f.same_layout = 1;\n\n        f.apply_trns = 0; /* not applicable */\n    }\n    else if(fmt & (SPNG_FMT_PNG | SPNG_FMT_RAW))\n    {\n        f.same_layout = 1;\n        f.do_scaling = 0;\n        f.apply_gamma = 0; /* for now */\n        f.apply_trns = 0;\n    }\n    else if(fmt == SPNG_FMT_G8 && ihdr->color_type == SPNG_COLOR_TYPE_GRAYSCALE && ihdr->bit_depth <= 8)\n    {\n        if(ihdr->bit_depth == depth_target) f.same_layout = 1;\n        else if(ihdr->bit_depth < 8) f.unpack = 1;\n\n        f.apply_trns = 0;\n    }\n    else if(fmt == SPNG_FMT_GA8 && ihdr->color_type == SPNG_COLOR_TYPE_GRAYSCALE && ihdr->bit_depth <= 8)\n    {\n        if(ihdr->color_type == SPNG_COLOR_TYPE_GRAYSCALE_ALPHA &&\n           ihdr->bit_depth == depth_target) f.same_layout = 1;\n        else if(ihdr->bit_depth <= 8) f.unpack = 1;\n    }\n    else if(fmt == SPNG_FMT_GA16 && ihdr->color_type == SPNG_COLOR_TYPE_GRAYSCALE && ihdr->bit_depth == 16)\n    {\n        if(ihdr->color_type == SPNG_COLOR_TYPE_GRAYSCALE_ALPHA &&\n           ihdr->bit_depth == depth_target) f.same_layout = 1;\n        else if(ihdr->bit_depth == 16) f.unpack = 1;\n    }\n\n    /*if(f.same_layout && !flags && !f.interlaced) f.zerocopy = 1;*/\n\n    uint16_t *gamma_lut = NULL;\n\n    if(f.apply_gamma)\n    {\n        float file_gamma = (float)ctx->gama / 100000.0f;\n        float max;\n\n        unsigned lut_entries;\n\n        if(fmt & (SPNG_FMT_RGBA8 | SPNG_FMT_RGB8))\n        {\n            lut_entries = 256;\n            max = 255.0f;\n\n            gamma_lut = ctx->gamma_lut8;\n            ctx->gamma_lut = ctx->gamma_lut8;\n        }\n        else /* SPNG_FMT_RGBA16 */\n        {\n            lut_entries = 65536;\n            max = 65535.0f;\n\n            ctx->gamma_lut16 = spng__malloc(ctx, lut_entries * sizeof(uint16_t));\n            if(ctx->gamma_lut16 == NULL) return decode_err(ctx, SPNG_EMEM);\n\n            gamma_lut = ctx->gamma_lut16;\n            ctx->gamma_lut = ctx->gamma_lut16;\n        }\n\n        float screen_gamma = 2.2f;\n        float exponent = file_gamma * screen_gamma;\n\n        if(FP_ZERO == fpclassify(exponent)) return decode_err(ctx, SPNG_EGAMA);\n\n        exponent = 1.0f / exponent;\n\n        unsigned i;\n        for(i=0; i < lut_entries; i++)\n        {\n            float c = pow((float)i / max, exponent) * max;\n            if(c > max) c = max;\n\n            gamma_lut[i] = (uint16_t)c;\n        }\n    }\n\n    struct spng_sbit *sb = &ctx->decode_sb;\n\n    sb->red_bits = processing_depth;\n    sb->green_bits = processing_depth;\n    sb->blue_bits = processing_depth;\n    sb->alpha_bits = processing_depth;\n    sb->grayscale_bits = processing_depth;\n\n    if(f.use_sbit)\n    {\n        if(ihdr->color_type == 0)\n        {\n            sb->grayscale_bits = ctx->sbit.grayscale_bits;\n            sb->alpha_bits = ihdr->bit_depth;\n        }\n        else if(ihdr->color_type == 2 || ihdr->color_type == 3)\n        {\n            sb->red_bits = ctx->sbit.red_bits;\n            sb->green_bits = ctx->sbit.green_bits;\n            sb->blue_bits = ctx->sbit.blue_bits;\n            sb->alpha_bits = ihdr->bit_depth;\n        }\n        else if(ihdr->color_type == 4)\n        {\n            sb->grayscale_bits = ctx->sbit.grayscale_bits;\n            sb->alpha_bits = ctx->sbit.alpha_bits;\n        }\n        else /* == 6 */\n        {\n            sb->red_bits = ctx->sbit.red_bits;\n            sb->green_bits = ctx->sbit.green_bits;\n            sb->blue_bits = ctx->sbit.blue_bits;\n            sb->alpha_bits = ctx->sbit.alpha_bits;\n        }\n    }\n\n    if(ihdr->bit_depth == 16 && fmt & (SPNG_FMT_RGBA8 | SPNG_FMT_RGB8))\n    {/* samples are scaled down by 8 bits in the decode loop */\n        sb->red_bits -= 8;\n        sb->green_bits -= 8;\n        sb->blue_bits -= 8;\n        sb->alpha_bits -= 8;\n        sb->grayscale_bits -= 8;\n\n        processing_depth = 8;\n    }\n\n    /* Prevent infinite loops in sample_to_target() */\n    if(!depth_target || depth_target > 16 ||\n       !processing_depth || processing_depth > 16 ||\n       !sb->grayscale_bits || sb->grayscale_bits > processing_depth ||\n       !sb->alpha_bits || sb->alpha_bits > processing_depth ||\n       !sb->red_bits || sb->red_bits > processing_depth ||\n       !sb->green_bits || sb->green_bits > processing_depth ||\n       !sb->blue_bits || sb->blue_bits > processing_depth)\n    {\n        return decode_err(ctx, SPNG_ESBIT);\n    }\n\n    if(sb->red_bits == sb->green_bits &&\n       sb->green_bits == sb->blue_bits &&\n       sb->blue_bits == sb->alpha_bits &&\n       sb->alpha_bits == processing_depth &&\n       processing_depth == depth_target) f.do_scaling = 0;\n\n    struct spng_plte_entry *plte = ctx->decode_plte.rgba;\n\n    /* Pre-process palette entries */\n    if(f.indexed)\n    {\n        uint8_t red, green, blue, alpha;\n\n        uint32_t i;\n        for(i=0; i < 256; i++)\n        {\n            if(f.apply_trns && i < ctx->trns.n_type3_entries)\n                ctx->plte.entries[i].alpha = ctx->trns.type3_alpha[i];\n            else\n                ctx->plte.entries[i].alpha = 255;\n\n            red   = sample_to_target(ctx->plte.entries[i].red, 8, sb->red_bits, 8);\n            green = sample_to_target(ctx->plte.entries[i].green, 8, sb->green_bits, 8);\n            blue  = sample_to_target(ctx->plte.entries[i].blue, 8, sb->blue_bits, 8);\n            alpha = sample_to_target(ctx->plte.entries[i].alpha, 8, sb->alpha_bits, 8);\n\n#if defined(SPNG_ARM)\n            if(fmt == SPNG_FMT_RGB8 && ihdr->bit_depth == 8)\n            {/* Working with 3 bytes at a time is more of an ARM thing */\n                ctx->decode_plte.rgb[i * 3 + 0] = red;\n                ctx->decode_plte.rgb[i * 3 + 1] = green;\n                ctx->decode_plte.rgb[i * 3 + 2] = blue;\n                continue;\n            }\n#endif\n            plte[i].red = red;\n            plte[i].green = green;\n            plte[i].blue = blue;\n            plte[i].alpha = alpha;\n        }\n\n        f.apply_trns = 0;\n    }\n\n    unsigned char *trns_px = ctx->trns_px;\n\n    if(f.apply_trns)\n    {\n        uint16_t mask = ~0;\n        if(ctx->ihdr.bit_depth < 16) mask = (1 << ctx->ihdr.bit_depth) - 1;\n\n        if(fmt & (SPNG_FMT_RGBA8 | SPNG_FMT_RGBA16))\n        {\n            if(ihdr->color_type == SPNG_COLOR_TYPE_TRUECOLOR)\n            {\n                if(ihdr->bit_depth == 16)\n                {\n                    memcpy(trns_px, &ctx->trns.red, 2);\n                    memcpy(trns_px + 2, &ctx->trns.green, 2);\n                    memcpy(trns_px + 4, &ctx->trns.blue, 2);\n                }\n                else\n                {\n                    trns_px[0] = ctx->trns.red & mask;\n                    trns_px[1] = ctx->trns.green & mask;\n                    trns_px[2] = ctx->trns.blue & mask;\n                }\n            }\n        }\n        else if(ihdr->color_type == SPNG_COLOR_TYPE_GRAYSCALE) // fmt == SPNG_FMT_GA8 &&\n        {\n            if(ihdr->bit_depth == 16)\n            {\n                memcpy(trns_px, &ctx->trns.gray, 2);\n            }\n            else\n            {\n                trns_px[0] = ctx->trns.gray & mask;\n            }\n        }\n    }\n\n    ctx->decode_flags = f;\n\n    ctx->state = SPNG_STATE_DECODE_INIT;\n\n    struct spng_row_info *ri = &ctx->row_info;\n    struct spng_subimage *sub = ctx->subimage;\n\n    while(!sub[ri->pass].width || !sub[ri->pass].height) ri->pass++;\n\n    if(f.interlaced) ri->row_num = adam7_y_start[ri->pass];\n\n    unsigned pixel_size = 4; /* SPNG_FMT_RGBA8 */\n\n    if(fmt == SPNG_FMT_RGBA16) pixel_size = 8;\n    else if(fmt == SPNG_FMT_RGB8) pixel_size = 3;\n    else if(fmt == SPNG_FMT_G8) pixel_size = 1;\n    else if(fmt == SPNG_FMT_GA8) pixel_size = 2;\n\n    int i;\n    for(i=ri->pass; i <= ctx->last_pass; i++)\n    {\n        if(!sub[i].scanline_width) continue;\n\n        if(fmt & (SPNG_FMT_PNG | SPNG_FMT_RAW)) sub[i].out_width = sub[i].scanline_width - 1;\n        else sub[i].out_width = (size_t)sub[i].width * pixel_size;\n\n        if(sub[i].out_width > UINT32_MAX) return decode_err(ctx, SPNG_EOVERFLOW);\n    }\n\n    /* Read the first filter byte, offsetting all reads by 1 byte.\n    The scanlines will be aligned with the start of the array with\n    the next scanline's filter byte at the end,\n    the last scanline will end up being 1 byte \"shorter\". */\n    ret = read_scanline_bytes(ctx, &ri->filter, 1);\n    if(ret) return decode_err(ctx, ret);\n\n    if(ri->filter > 4) return decode_err(ctx, SPNG_EFILTER);\n\n    if(flags & SPNG_DECODE_PROGRESSIVE)\n    {\n        return 0;\n    }\n\n    do\n    {\n        size_t ioffset = ri->row_num * ctx->image_width;\n\n        ret = spng_decode_row(ctx, (unsigned char*)out + ioffset, ctx->image_width);\n    }while(!ret);\n\n    if(ret != SPNG_EOI) return decode_err(ctx, ret);\n\n    return 0;\n}\n\nint spng_get_row_info(spng_ctx *ctx, struct spng_row_info *row_info)\n{\n    if(ctx == NULL || row_info == NULL || ctx->state < SPNG_STATE_DECODE_INIT) return 1;\n\n    if(ctx->state >= SPNG_STATE_EOI) return SPNG_EOI;\n\n    *row_info = ctx->row_info;\n\n    return 0;\n}\n\nstatic int write_chunks_before_idat(spng_ctx *ctx)\n{\n    if(ctx == NULL) return SPNG_EINTERNAL;\n    if(!ctx->encode_only) return SPNG_EINTERNAL;\n    if(!ctx->stored.ihdr) return SPNG_EINTERNAL;\n\n    int ret;\n    uint32_t i;\n    size_t length;\n    const struct spng_ihdr *ihdr = &ctx->ihdr;\n    unsigned char *data = ctx->decode_plte.raw;\n\n    ret = write_data(ctx, spng_signature, 8);\n    if(ret) return ret;\n\n    write_u32(data,     ihdr->width);\n    write_u32(data + 4, ihdr->height);\n    data[8]  = ihdr->bit_depth;\n    data[9]  = ihdr->color_type;\n    data[10] = ihdr->compression_method;\n    data[11] = ihdr->filter_method;\n    data[12] = ihdr->interlace_method;\n\n    ret = write_chunk(ctx, type_ihdr, data, 13);\n    if(ret) return ret;\n\n    if(ctx->stored.chrm)\n    {\n        write_u32(data,      ctx->chrm_int.white_point_x);\n        write_u32(data + 4,  ctx->chrm_int.white_point_y);\n        write_u32(data + 8,  ctx->chrm_int.red_x);\n        write_u32(data + 12, ctx->chrm_int.red_y);\n        write_u32(data + 16, ctx->chrm_int.green_x);\n        write_u32(data + 20, ctx->chrm_int.green_y);\n        write_u32(data + 24, ctx->chrm_int.blue_x);\n        write_u32(data + 28, ctx->chrm_int.blue_y);\n\n        ret = write_chunk(ctx, type_chrm, data, 32);\n        if(ret) return ret;\n    }\n\n    if(ctx->stored.gama)\n    {\n        write_u32(data, ctx->gama);\n\n        ret = write_chunk(ctx, type_gama, data, 4);\n        if(ret) return ret;\n    }\n\n    if(ctx->stored.iccp)\n    {\n        uLongf dest_len = compressBound((uLong)ctx->iccp.profile_len);\n\n        Bytef *buf = spng__malloc(ctx, dest_len);\n        if(buf == NULL) return SPNG_EMEM;\n\n        ret = compress2(buf, &dest_len, (void*)ctx->iccp.profile, (uLong)ctx->iccp.profile_len, Z_DEFAULT_COMPRESSION);\n\n        if(ret != Z_OK)\n        {\n            spng__free(ctx, buf);\n            return SPNG_EZLIB;\n        }\n\n        size_t name_len = strlen(ctx->iccp.profile_name);\n\n        length = name_len + 2;\n        length += dest_len;\n\n        if(dest_len > length) return SPNG_EOVERFLOW;\n\n        unsigned char *cdata = NULL;\n\n        ret = write_header(ctx, type_iccp, length, &cdata);\n\n        if(ret)\n        {\n            spng__free(ctx, buf);\n            return ret;\n        }\n\n        memcpy(cdata, ctx->iccp.profile_name, name_len + 1);\n        cdata[name_len + 1] = 0; /* compression method */\n        memcpy(cdata + name_len + 2, buf, dest_len);\n\n        spng__free(ctx, buf);\n\n        ret = finish_chunk(ctx);\n        if(ret) return ret;\n    }\n\n    if(ctx->stored.sbit)\n    {\n        switch(ctx->ihdr.color_type)\n        {\n            case SPNG_COLOR_TYPE_GRAYSCALE:\n            {\n                length = 1;\n\n                data[0] = ctx->sbit.grayscale_bits;\n\n                break;\n            }\n            case SPNG_COLOR_TYPE_TRUECOLOR:\n            case SPNG_COLOR_TYPE_INDEXED:\n            {\n                length = 3;\n\n                data[0] = ctx->sbit.red_bits;\n                data[1] = ctx->sbit.green_bits;\n                data[2] = ctx->sbit.blue_bits;\n\n                break;\n            }\n            case SPNG_COLOR_TYPE_GRAYSCALE_ALPHA:\n            {\n                length = 2;\n\n                data[0] = ctx->sbit.grayscale_bits;\n                data[1] = ctx->sbit.alpha_bits;\n\n                break;\n            }\n            case SPNG_COLOR_TYPE_TRUECOLOR_ALPHA:\n            {\n                length = 4;\n\n                data[0] = ctx->sbit.red_bits;\n                data[1] = ctx->sbit.green_bits;\n                data[2] = ctx->sbit.blue_bits;\n                data[3] = ctx->sbit.alpha_bits;\n\n                break;\n            }\n            default: return SPNG_EINTERNAL;\n        }\n\n        ret = write_chunk(ctx, type_sbit, data, length);\n        if(ret) return ret;\n    }\n\n    if(ctx->stored.srgb)\n    {\n        ret = write_chunk(ctx, type_srgb, &ctx->srgb_rendering_intent, 1);\n        if(ret) return ret;\n    }\n\n    ret = write_unknown_chunks(ctx, SPNG_AFTER_IHDR);\n    if(ret) return ret;\n\n    if(ctx->stored.plte)\n    {\n        for(i=0; i < ctx->plte.n_entries; i++)\n        {\n            data[i * 3 + 0] = ctx->plte.entries[i].red;\n            data[i * 3 + 1] = ctx->plte.entries[i].green;\n            data[i * 3 + 2] = ctx->plte.entries[i].blue;\n        }\n\n        ret = write_chunk(ctx, type_plte, data, ctx->plte.n_entries * 3);\n        if(ret) return ret;\n    }\n\n    if(ctx->stored.bkgd)\n    {\n        switch(ctx->ihdr.color_type)\n        {\n            case SPNG_COLOR_TYPE_GRAYSCALE:\n            case SPNG_COLOR_TYPE_GRAYSCALE_ALPHA:\n            {\n                length = 2;\n\n                write_u16(data, ctx->bkgd.gray);\n\n                break;\n            }\n            case SPNG_COLOR_TYPE_TRUECOLOR:\n            case SPNG_COLOR_TYPE_TRUECOLOR_ALPHA:\n            {\n                length = 6;\n\n                write_u16(data,     ctx->bkgd.red);\n                write_u16(data + 2, ctx->bkgd.green);\n                write_u16(data + 4, ctx->bkgd.blue);\n\n                break;\n            }\n            case SPNG_COLOR_TYPE_INDEXED:\n            {\n                length = 1;\n\n                data[0] = ctx->bkgd.plte_index;\n\n                break;\n            }\n            default: return SPNG_EINTERNAL;\n        }\n\n        ret = write_chunk(ctx, type_bkgd, data, length);\n        if(ret) return ret;\n    }\n\n    if(ctx->stored.hist)\n    {\n        length = ctx->plte.n_entries * 2;\n\n        for(i=0; i < ctx->plte.n_entries; i++)\n        {\n            write_u16(data + i * 2, ctx->hist.frequency[i]);\n        }\n\n        ret = write_chunk(ctx, type_hist, data, length);\n        if(ret) return ret;\n    }\n\n    if(ctx->stored.trns)\n    {\n        if(ctx->ihdr.color_type == SPNG_COLOR_TYPE_GRAYSCALE)\n        {\n            write_u16(data, ctx->trns.gray);\n\n            ret = write_chunk(ctx, type_trns, data, 2);\n        }\n        else if(ctx->ihdr.color_type == SPNG_COLOR_TYPE_TRUECOLOR)\n        {\n            write_u16(data,     ctx->trns.red);\n            write_u16(data + 2, ctx->trns.green);\n            write_u16(data + 4, ctx->trns.blue);\n\n            ret = write_chunk(ctx, type_trns, data, 6);\n        }\n        else if(ctx->ihdr.color_type == SPNG_COLOR_TYPE_INDEXED)\n        {\n            ret = write_chunk(ctx, type_trns, ctx->trns.type3_alpha, ctx->trns.n_type3_entries);\n        }\n\n        if(ret) return ret;\n    }\n\n    if(ctx->stored.phys)\n    {\n        write_u32(data,     ctx->phys.ppu_x);\n        write_u32(data + 4, ctx->phys.ppu_y);\n        data[8] = ctx->phys.unit_specifier;\n\n        ret = write_chunk(ctx, type_phys, data, 9);\n        if(ret) return ret;\n    }\n\n    if(ctx->stored.splt)\n    {\n        const struct spng_splt *splt;\n        unsigned char *cdata = NULL;\n\n        uint32_t k;\n        for(i=0; i < ctx->n_splt; i++)\n        {\n            splt = &ctx->splt_list[i];\n\n            size_t name_len = strlen(splt->name);\n            length = name_len + 1;\n\n            if(splt->sample_depth == 8) length += splt->n_entries * 6 + 1;\n            else if(splt->sample_depth == 16) length += splt->n_entries * 10 + 1;\n\n            ret = write_header(ctx, type_splt, length, &cdata);\n            if(ret) return ret;\n\n            memcpy(cdata, splt->name, name_len + 1);\n            cdata += name_len + 2;\n            cdata[-1] = splt->sample_depth;\n\n            if(splt->sample_depth == 8)\n            {\n                for(k=0; k < splt->n_entries; k++)\n                {\n                    cdata[k * 6 + 0] = splt->entries[k].red;\n                    cdata[k * 6 + 1] = splt->entries[k].green;\n                    cdata[k * 6 + 2] = splt->entries[k].blue;\n                    cdata[k * 6 + 3] = splt->entries[k].alpha;\n                    write_u16(cdata + k * 6 + 4, splt->entries[k].frequency);\n                }\n            }\n            else if(splt->sample_depth == 16)\n            {\n                for(k=0; k < splt->n_entries; k++)\n                {\n                    write_u16(cdata + k * 10 + 0, splt->entries[k].red);\n                    write_u16(cdata + k * 10 + 2, splt->entries[k].green);\n                    write_u16(cdata + k * 10 + 4, splt->entries[k].blue);\n                    write_u16(cdata + k * 10 + 6, splt->entries[k].alpha);\n                    write_u16(cdata + k * 10 + 8, splt->entries[k].frequency);\n                }\n            }\n\n            ret = finish_chunk(ctx);\n            if(ret) return ret;\n        }\n    }\n\n    if(ctx->stored.time)\n    {\n        write_u16(data, ctx->time.year);\n        data[2] = ctx->time.month;\n        data[3] = ctx->time.day;\n        data[4] = ctx->time.hour;\n        data[5] = ctx->time.minute;\n        data[6] = ctx->time.second;\n\n        ret = write_chunk(ctx, type_time, data, 7);\n        if(ret) return ret;\n    }\n\n    if(ctx->stored.text)\n    {\n        unsigned char *cdata = NULL;\n        const struct spng_text2 *text;\n        const uint8_t *text_type_array[4] = { 0, type_text, type_ztxt, type_itxt };\n\n        for(i=0; i < ctx->n_text; i++)\n        {\n            text = &ctx->text_list[i];\n\n            const uint8_t *text_chunk_type = text_type_array[text->type];\n            Bytef *compressed_text = NULL;\n            size_t keyword_len = 0;\n            size_t language_tag_len = 0;\n            size_t translated_keyword_len = 0;\n            size_t compressed_length = 0;\n            size_t text_length = 0;\n\n            keyword_len = strlen(text->keyword);\n            text_length = strlen(text->text);\n\n            length = keyword_len + 1;\n\n            if(text->type == SPNG_ZTXT)\n            {\n                length += 1; /* compression method */\n            }\n            else if(text->type == SPNG_ITXT)\n            {\n                if(!text->language_tag || !text->translated_keyword) return SPNG_EINTERNAL;\n\n                language_tag_len = strlen(text->language_tag);\n                translated_keyword_len = strlen(text->translated_keyword);\n\n                length += language_tag_len;\n                if(length < language_tag_len) return SPNG_EOVERFLOW;\n\n                length += translated_keyword_len;\n                if(length < translated_keyword_len) return SPNG_EOVERFLOW;\n\n                length += 4; /* compression flag + method + nul for the two strings */\n                if(length < 4) return SPNG_EOVERFLOW;\n            }\n\n            if(text->compression_flag)\n            {\n                ret = spng__deflate_init(ctx, &ctx->text_options);\n                if(ret) return ret;\n\n                z_stream *zstream = &ctx->zstream;\n                uLongf dest_len = deflateBound(zstream, (uLong)text_length);\n\n                compressed_text = spng__malloc(ctx, dest_len);\n\n                if(compressed_text == NULL) return SPNG_EMEM;\n\n                zstream->next_in = (void*)text->text;\n                zstream->avail_in = (uInt)text_length;\n\n                zstream->next_out = compressed_text;\n                zstream->avail_out = dest_len;\n\n                ret = deflate(zstream, Z_FINISH);\n\n                if(ret != Z_STREAM_END)\n                {\n                    spng__free(ctx, compressed_text);\n                    return SPNG_EZLIB;\n                }\n\n                compressed_length = zstream->total_out;\n\n                length += compressed_length;\n                if(length < compressed_length) return SPNG_EOVERFLOW;\n            }\n            else\n            {\n                text_length = strlen(text->text);\n\n                length += text_length;\n                if(length < text_length) return SPNG_EOVERFLOW;\n            }\n\n            ret = write_header(ctx, text_chunk_type, length, &cdata);\n            if(ret)\n            {\n                spng__free(ctx, compressed_text);\n                return ret;\n            }\n\n            memcpy(cdata, text->keyword, keyword_len + 1);\n            cdata += keyword_len + 1;\n\n            if(text->type == SPNG_ITXT)\n            {\n                cdata[0] = text->compression_flag;\n                cdata[1] = 0; /* compression method */\n                cdata += 2;\n\n                memcpy(cdata, text->language_tag, language_tag_len + 1);\n                cdata += language_tag_len + 1;\n\n                memcpy(cdata, text->translated_keyword, translated_keyword_len + 1);\n                cdata += translated_keyword_len + 1;\n            }\n            else if(text->type == SPNG_ZTXT)\n            {\n                cdata[0] = 0; /* compression method */\n                cdata++;\n            }\n\n            if(text->compression_flag) memcpy(cdata, compressed_text, compressed_length);\n            else memcpy(cdata, text->text, text_length);\n\n            spng__free(ctx, compressed_text);\n\n            ret = finish_chunk(ctx);\n            if(ret) return ret;\n        }\n    }\n\n    if(ctx->stored.offs)\n    {\n        write_s32(data,     ctx->offs.x);\n        write_s32(data + 4, ctx->offs.y);\n        data[8] = ctx->offs.unit_specifier;\n\n        ret = write_chunk(ctx, type_offs, data, 9);\n        if(ret) return ret;\n    }\n\n    if(ctx->stored.exif)\n    {\n        ret = write_chunk(ctx, type_exif, ctx->exif.data, ctx->exif.length);\n        if(ret) return ret;\n    }\n\n    ret = write_unknown_chunks(ctx, SPNG_AFTER_PLTE);\n    if(ret) return ret;\n\n    return 0;\n}\n\nstatic int write_chunks_after_idat(spng_ctx *ctx)\n{\n    if(ctx == NULL) return SPNG_EINTERNAL;\n\n    int ret = write_unknown_chunks(ctx, SPNG_AFTER_IDAT);\n    if(ret) return ret;\n\n    return write_iend(ctx);\n}\n\n/* Compress and write scanline to IDAT stream */\nstatic int write_idat_bytes(spng_ctx *ctx, const void *scanline, size_t len, int flush)\n{\n    if(ctx == NULL || scanline == NULL) return SPNG_EINTERNAL;\n    if(len > UINT_MAX) return SPNG_EINTERNAL;\n\n    int ret = 0;\n    unsigned char *data = NULL;\n    z_stream *zstream = &ctx->zstream;\n    uint32_t idat_length = SPNG_WRITE_SIZE;\n\n    zstream->next_in = scanline;\n    zstream->avail_in = (uInt)len;\n\n    do\n    {\n        ret = deflate(zstream, flush);\n\n        if(zstream->avail_out == 0)\n        {\n            ret = finish_chunk(ctx);\n            if(ret) return encode_err(ctx, ret);\n\n            ret = write_header(ctx, type_idat, idat_length, &data);\n            if(ret) return encode_err(ctx, ret);\n\n            zstream->next_out = data;\n            zstream->avail_out = idat_length;\n        }\n\n    }while(zstream->avail_in);\n\n    if(ret != Z_OK) return SPNG_EZLIB;\n\n    return 0;\n}\n\nstatic int finish_idat(spng_ctx *ctx)\n{\n    int ret = 0;\n    unsigned char *data = NULL;\n    z_stream *zstream = &ctx->zstream;\n    uint32_t idat_length = SPNG_WRITE_SIZE;\n\n    while(ret != Z_STREAM_END)\n    {\n        ret = deflate(zstream, Z_FINISH);\n\n        if(ret)\n        {\n            if(ret == Z_STREAM_END) break;\n\n            if(ret != Z_BUF_ERROR) return SPNG_EZLIB;\n        }\n\n        if(zstream->avail_out == 0)\n        {\n            ret = finish_chunk(ctx);\n            if(ret) return encode_err(ctx, ret);\n\n            ret = write_header(ctx, type_idat, idat_length, &data);\n            if(ret) return encode_err(ctx, ret);\n\n            zstream->next_out = data;\n            zstream->avail_out = idat_length;\n        }\n    }\n\n    uint32_t trimmed_length = idat_length - zstream->avail_out;\n\n    ret = trim_chunk(ctx, trimmed_length);\n    if(ret) return ret;\n\n    return finish_chunk(ctx);\n}\n\nstatic int encode_scanline(spng_ctx *ctx, const void *scanline, size_t len)\n{\n    if(ctx == NULL || scanline == NULL) return SPNG_EINTERNAL;\n\n    int ret, pass = ctx->row_info.pass;\n    uint8_t filter = 0;\n    struct spng_row_info *ri = &ctx->row_info;\n    const struct spng_subimage *sub = ctx->subimage;\n    struct encode_flags f = ctx->encode_flags;\n    unsigned char *filtered_scanline = ctx->filtered_scanline;\n    size_t scanline_width = sub[pass].scanline_width;\n\n    if(len < scanline_width - 1) return SPNG_EINTERNAL;\n\n    /* encode_row() interlaces directly to ctx->scanline */\n    if(scanline != ctx->scanline) memcpy(ctx->scanline, scanline, scanline_width - 1);\n\n    if(f.to_bigendian) u16_row_to_bigendian(ctx->scanline, scanline_width - 1);\n    const int requires_previous = f.filter_choice & (SPNG_FILTER_CHOICE_UP | SPNG_FILTER_CHOICE_AVG | SPNG_FILTER_CHOICE_PAETH);\n\n    /* XXX: exclude 'requires_previous' filters by default for first scanline? */\n    if(!ri->scanline_idx && requires_previous)\n    {\n        /* prev_scanline is all zeros for the first scanline */\n        memset(ctx->prev_scanline, 0, scanline_width);\n    }\n\n    filter = get_best_filter(ctx->prev_scanline, ctx->scanline, scanline_width, ctx->bytes_per_pixel, f.filter_choice);\n\n    if(!filter) filtered_scanline = ctx->scanline;\n\n    filtered_scanline[-1] = filter;\n\n    if(filter)\n    {\n        ret = filter_scanline(filtered_scanline, ctx->prev_scanline, ctx->scanline, scanline_width, ctx->bytes_per_pixel, filter);\n        if(ret) return encode_err(ctx, ret);\n    }\n\n    ret = write_idat_bytes(ctx, filtered_scanline - 1, scanline_width, Z_NO_FLUSH);\n    if(ret) return encode_err(ctx, ret);\n\n    /* The previous scanline is always unfiltered */\n    void *t = ctx->prev_scanline;\n    ctx->prev_scanline = ctx->scanline;\n    ctx->scanline = t;\n\n    ret = update_row_info(ctx);\n\n    if(ret == SPNG_EOI)\n    {\n        int error = finish_idat(ctx);\n        if(error) encode_err(ctx, error);\n\n        if(f.finalize)\n        {\n            error = spng_encode_chunks(ctx);\n            if(error) return encode_err(ctx, error);\n        }\n    }\n\n    return ret;\n}\n\nstatic int encode_row(spng_ctx *ctx, const void *row, size_t len)\n{\n    if(ctx == NULL || row == NULL) return SPNG_EINTERNAL;\n\n    const int pass = ctx->row_info.pass;\n\n    if(!ctx->ihdr.interlace_method || pass == 6) return encode_scanline(ctx, row, len);\n\n    uint32_t k;\n    const unsigned pixel_size = ctx->pixel_size;\n    const unsigned bit_depth = ctx->ihdr.bit_depth;\n\n    if(bit_depth < 8)\n    {\n        const unsigned samples_per_byte = 8 / bit_depth;\n        const uint8_t mask = (1 << bit_depth) - 1;\n        const unsigned initial_shift = 8 - bit_depth;\n        unsigned shift_amount = initial_shift;\n\n        unsigned char *scanline = ctx->scanline;\n        const unsigned char *row_uc = row;\n        uint8_t sample;\n\n        memset(scanline, 0, ctx->subimage[pass].scanline_width);\n\n        for(k=0; k < ctx->subimage[pass].width; k++)\n        {\n            size_t ioffset = adam7_x_start[pass] + k * adam7_x_delta[pass];\n\n            sample = row_uc[ioffset / samples_per_byte];\n\n            sample = sample >> (initial_shift - ioffset * bit_depth % 8);\n            sample = sample & mask;\n            sample = sample << shift_amount;\n\n            scanline[0] |= sample;\n\n            shift_amount -= bit_depth;\n\n            if(shift_amount > 7)\n            {\n                shift_amount = initial_shift;\n                scanline++;\n            }\n        }\n\n        return encode_scanline(ctx, ctx->scanline, len);\n    }\n\n    for(k=0; k < ctx->subimage[pass].width; k++)\n    {\n        size_t ioffset = (adam7_x_start[pass] + (size_t) k * adam7_x_delta[pass]) * pixel_size;\n\n        memcpy(ctx->scanline + k * pixel_size, (unsigned char*)row + ioffset, pixel_size);\n    }\n\n    return encode_scanline(ctx, ctx->scanline, len);\n}\n\nint spng_encode_scanline(spng_ctx *ctx, const void *scanline, size_t len)\n{\n    if(ctx == NULL || scanline == NULL) return SPNG_EINVAL;\n    if(ctx->state >= SPNG_STATE_EOI) return SPNG_EOI;\n    if(len < (ctx->subimage[ctx->row_info.pass].scanline_width -1) ) return SPNG_EBUFSIZ;\n\n    return encode_scanline(ctx, scanline, len);\n}\n\nint spng_encode_row(spng_ctx *ctx, const void *row, size_t len)\n{\n    if(ctx == NULL || row == NULL) return SPNG_EINVAL;\n    if(ctx->state >= SPNG_STATE_EOI) return SPNG_EOI;\n    if(len < ctx->image_width) return SPNG_EBUFSIZ;\n\n    return encode_row(ctx, row, len);\n}\n\nint spng_encode_chunks(spng_ctx *ctx)\n{\n    if(ctx == NULL) return 1;\n    if(!ctx->state) return SPNG_EBADSTATE;\n    if(ctx->state < SPNG_STATE_OUTPUT) return SPNG_ENODST;\n    if(!ctx->encode_only) return SPNG_ECTXTYPE;\n\n    int ret = 0;\n\n    if(ctx->state < SPNG_STATE_FIRST_IDAT)\n    {\n        if(!ctx->stored.ihdr) return SPNG_ENOIHDR;\n\n        ret = write_chunks_before_idat(ctx);\n        if(ret) return encode_err(ctx, ret);\n\n        ctx->state = SPNG_STATE_FIRST_IDAT;\n    }\n    else if(ctx->state == SPNG_STATE_FIRST_IDAT)\n    {\n        return 0;\n    }\n    else if(ctx->state == SPNG_STATE_EOI)\n    {\n        ret = write_chunks_after_idat(ctx);\n        if(ret) return encode_err(ctx, ret);\n\n        ctx->state = SPNG_STATE_IEND;\n    }\n    else return SPNG_EOPSTATE;\n\n    return 0;\n}\n\nint spng_encode_image(spng_ctx *ctx, const void *img, size_t len, int fmt, int flags)\n{\n    if(ctx == NULL) return 1;\n    if(!ctx->state) return SPNG_EBADSTATE;\n    if(!ctx->encode_only) return SPNG_ECTXTYPE;\n    if(!ctx->stored.ihdr) return SPNG_ENOIHDR;\n    if( !(fmt == SPNG_FMT_PNG || fmt == SPNG_FMT_RAW) ) return SPNG_EFMT;\n\n    int ret = 0;\n    const struct spng_ihdr *ihdr = &ctx->ihdr;\n    struct encode_flags *encode_flags = &ctx->encode_flags;\n\n    if(ihdr->color_type == SPNG_COLOR_TYPE_INDEXED && !ctx->stored.plte) return SPNG_ENOPLTE;\n\n    ret = calculate_image_width(ihdr, fmt, &ctx->image_width);\n    if(ret) return encode_err(ctx, ret);\n\n    if(ctx->image_width > SIZE_MAX / ihdr->height) ctx->image_size = 0; /* overflow */\n    else ctx->image_size = ctx->image_width * ihdr->height;\n\n    if( !(flags & SPNG_ENCODE_PROGRESSIVE) )\n    {\n        if(img == NULL) return 1;\n        if(!ctx->image_size) return SPNG_EOVERFLOW;\n        if(len != ctx->image_size) return SPNG_EBUFSIZ;\n    }\n\n    ret = spng_encode_chunks(ctx);\n    if(ret) return encode_err(ctx, ret);\n\n    ret = calculate_subimages(ctx);\n    if(ret) return encode_err(ctx, ret);\n\n    if(ihdr->bit_depth < 8) ctx->bytes_per_pixel = 1;\n    else ctx->bytes_per_pixel = num_channels(ihdr) * (ihdr->bit_depth / 8);\n\n    if(spng__optimize(SPNG_FILTER_CHOICE))\n    {\n        /* Filtering would make no difference */\n        if(!ctx->image_options.compression_level)\n        {\n            encode_flags->filter_choice = SPNG_DISABLE_FILTERING;\n        }\n\n        /* Palette indices and low bit-depth images do not benefit from filtering */\n        if(ihdr->color_type == SPNG_COLOR_TYPE_INDEXED || ihdr->bit_depth < 8)\n        {\n            encode_flags->filter_choice = SPNG_DISABLE_FILTERING;\n        }\n    }\n\n    /* This is technically the same as disabling filtering */\n    if(encode_flags->filter_choice == SPNG_FILTER_CHOICE_NONE)\n    {\n        encode_flags->filter_choice = SPNG_DISABLE_FILTERING;\n    }\n\n    if(!encode_flags->filter_choice && spng__optimize(SPNG_IMG_COMPRESSION_STRATEGY))\n    {\n        ctx->image_options.strategy = Z_DEFAULT_STRATEGY;\n    }\n\n    ret = spng__deflate_init(ctx, &ctx->image_options);\n    if(ret) return encode_err(ctx, ret);\n\n    size_t scanline_buf_size = ctx->subimage[ctx->widest_pass].scanline_width;\n\n    scanline_buf_size += 32;\n\n    if(scanline_buf_size < 32) return SPNG_EOVERFLOW;\n\n    ctx->scanline_buf = spng__malloc(ctx, scanline_buf_size);\n    ctx->prev_scanline_buf = spng__malloc(ctx, scanline_buf_size);\n\n    if(ctx->scanline_buf == NULL || ctx->prev_scanline_buf == NULL) return encode_err(ctx, SPNG_EMEM);\n\n    /* Maintain alignment for pixels, filter at [-1] */\n    ctx->scanline = ctx->scanline_buf + 16;\n    ctx->prev_scanline = ctx->prev_scanline_buf + 16;\n\n    if(encode_flags->filter_choice)\n    {\n        ctx->filtered_scanline_buf = spng__malloc(ctx, scanline_buf_size);\n        if(ctx->filtered_scanline_buf == NULL) return encode_err(ctx, SPNG_EMEM);\n\n        ctx->filtered_scanline = ctx->filtered_scanline_buf + 16;\n    }\n\n    struct spng_subimage *sub = ctx->subimage;\n    struct spng_row_info *ri = &ctx->row_info;\n\n    ctx->fmt = fmt;\n\n    z_stream *zstream = &ctx->zstream;\n    zstream->avail_out = SPNG_WRITE_SIZE;\n\n    ret = write_header(ctx, type_idat, zstream->avail_out, &zstream->next_out);\n    if(ret) return encode_err(ctx, ret);\n\n    if(ihdr->interlace_method) encode_flags->interlace = 1;\n\n    if(fmt & (SPNG_FMT_PNG | SPNG_FMT_RAW) ) encode_flags->same_layout = 1;\n\n    if(ihdr->bit_depth == 16 && fmt != SPNG_FMT_RAW) encode_flags->to_bigendian = 1;\n\n    if(flags & SPNG_ENCODE_FINALIZE) encode_flags->finalize = 1;\n\n    while(!sub[ri->pass].width || !sub[ri->pass].height) ri->pass++;\n\n    if(encode_flags->interlace) ri->row_num = adam7_y_start[ri->pass];\n\n    ctx->pixel_size = 4; /* SPNG_FMT_RGBA8 */\n\n    if(fmt == SPNG_FMT_RGBA16) ctx->pixel_size = 8;\n    else if(fmt == SPNG_FMT_RGB8) ctx->pixel_size = 3;\n    else if(fmt == SPNG_FMT_G8) ctx->pixel_size = 1;\n    else if(fmt == SPNG_FMT_GA8) ctx->pixel_size = 2;\n    else if(fmt & (SPNG_FMT_PNG | SPNG_FMT_RAW)) ctx->pixel_size = ctx->bytes_per_pixel;\n\n    ctx->state = SPNG_STATE_ENCODE_INIT;\n\n    if(flags & SPNG_ENCODE_PROGRESSIVE)\n    {\n        encode_flags->progressive = 1;\n\n        return 0;\n    }\n\n    do\n    {\n        size_t ioffset = ri->row_num * ctx->image_width;\n\n        ret = encode_row(ctx, (unsigned char*)img + ioffset, ctx->image_width);\n\n    }while(!ret);\n\n    if(ret != SPNG_EOI) return encode_err(ctx, ret);\n\n    return 0;\n}\n\nspng_ctx *spng_ctx_new(int flags)\n{\n    struct spng_alloc alloc =\n    {\n        .malloc_fn = malloc,\n        .realloc_fn = realloc,\n        .calloc_fn = calloc,\n        .free_fn = free\n    };\n\n    return spng_ctx_new2(&alloc, flags);\n}\n\nspng_ctx *spng_ctx_new2(struct spng_alloc *alloc, int flags)\n{\n    if(alloc == NULL) return NULL;\n    if(flags != (flags & SPNG__CTX_FLAGS_ALL)) return NULL;\n\n    if(alloc->malloc_fn == NULL) return NULL;\n    if(alloc->realloc_fn == NULL) return NULL;\n    if(alloc->calloc_fn == NULL) return NULL;\n    if(alloc->free_fn == NULL) return NULL;\n\n    spng_ctx *ctx = alloc->calloc_fn(1, sizeof(spng_ctx));\n    if(ctx == NULL) return NULL;\n\n    ctx->alloc = *alloc;\n\n    ctx->max_width = spng_u32max;\n    ctx->max_height = spng_u32max;\n\n    ctx->max_chunk_size = spng_u32max;\n    ctx->chunk_cache_limit = SIZE_MAX;\n    ctx->chunk_count_limit = SPNG_MAX_CHUNK_COUNT;\n\n    ctx->state = SPNG_STATE_INIT;\n\n    ctx->crc_action_critical = SPNG_CRC_ERROR;\n    ctx->crc_action_ancillary = SPNG_CRC_DISCARD;\n\n    const struct spng__zlib_options image_defaults =\n    {\n        .compression_level = Z_DEFAULT_COMPRESSION,\n        .window_bits = 15,\n        .mem_level = 8,\n        .strategy = Z_FILTERED,\n        .data_type = 0 /* Z_BINARY */\n    };\n\n    const struct spng__zlib_options text_defaults =\n    {\n        .compression_level = Z_DEFAULT_COMPRESSION,\n        .window_bits = 15,\n        .mem_level = 8,\n        .strategy = Z_DEFAULT_STRATEGY,\n        .data_type = 1 /* Z_TEXT */\n    };\n\n    ctx->image_options = image_defaults;\n    ctx->text_options = text_defaults;\n\n    ctx->optimize_option = ~0;\n    ctx->encode_flags.filter_choice = SPNG_FILTER_CHOICE_ALL;\n\n    ctx->flags = flags;\n\n    if(flags & SPNG_CTX_ENCODER) ctx->encode_only = 1;\n\n    return ctx;\n}\n\nvoid spng_ctx_free(spng_ctx *ctx)\n{\n    if(ctx == NULL) return;\n\n    if(ctx->streaming && ctx->stream_buf != NULL) spng__free(ctx, ctx->stream_buf);\n\n    if(!ctx->user.exif) spng__free(ctx, ctx->exif.data);\n\n    if(!ctx->user.iccp) spng__free(ctx, ctx->iccp.profile);\n\n    uint32_t i;\n\n    if(ctx->splt_list != NULL && !ctx->user.splt)\n    {\n        for(i=0; i < ctx->n_splt; i++)\n        {\n            spng__free(ctx, ctx->splt_list[i].entries);\n        }\n        spng__free(ctx, ctx->splt_list);\n    }\n\n    if(ctx->text_list != NULL)\n    {\n        for(i=0; i< ctx->n_text; i++)\n        {\n            if(ctx->user.text) break;\n\n            spng__free(ctx, ctx->text_list[i].keyword);\n            if(ctx->text_list[i].compression_flag) spng__free(ctx, ctx->text_list[i].text);\n        }\n        spng__free(ctx, ctx->text_list);\n    }\n\n    if(ctx->chunk_list != NULL && !ctx->user.unknown)\n    {\n        for(i=0; i< ctx->n_chunks; i++)\n        {\n            spng__free(ctx, ctx->chunk_list[i].data);\n        }\n        spng__free(ctx, ctx->chunk_list);\n    }\n\n    if(ctx->deflate) deflateEnd(&ctx->zstream);\n    else inflateEnd(&ctx->zstream);\n\n    if(!ctx->user_owns_out_png) spng__free(ctx, ctx->out_png);\n\n    spng__free(ctx, ctx->gamma_lut16);\n\n    spng__free(ctx, ctx->row_buf);\n    spng__free(ctx, ctx->scanline_buf);\n    spng__free(ctx, ctx->prev_scanline_buf);\n    spng__free(ctx, ctx->filtered_scanline_buf);\n\n    spng_free_fn *free_fn = ctx->alloc.free_fn;\n\n    memset(ctx, 0, sizeof(spng_ctx));\n\n    free_fn(ctx);\n}\n\nstatic int buffer_read_fn(spng_ctx *ctx, void *user, void *data, size_t n)\n{\n    if(n > ctx->bytes_left) return SPNG_IO_EOF;\n\n    (void)user;\n    (void)data;\n    ctx->data = ctx->data + ctx->last_read_size;\n\n    ctx->last_read_size = n;\n    ctx->bytes_left -= n;\n\n    return 0;\n}\n\nstatic int file_read_fn(spng_ctx *ctx, void *user, void *data, size_t n)\n{\n    FILE *file = user;\n    (void)ctx;\n\n    if(fread(data, n, 1, file) != 1)\n    {\n        if(feof(file)) return SPNG_IO_EOF;\n        else return SPNG_IO_ERROR;\n    }\n\n    return 0;\n}\n\nstatic int file_write_fn(spng_ctx *ctx, void *user, void *data, size_t n)\n{\n    FILE *file = user;\n    (void)ctx;\n\n    if(fwrite(data, n, 1, file) != 1) return SPNG_IO_ERROR;\n\n    return 0;\n}\n\nint spng_set_png_buffer(spng_ctx *ctx, const void *buf, size_t size)\n{\n    if(ctx == NULL || buf == NULL) return 1;\n    if(!ctx->state) return SPNG_EBADSTATE;\n    if(ctx->encode_only) return SPNG_ECTXTYPE; /* not supported */\n\n    if(ctx->data != NULL) return SPNG_EBUF_SET;\n\n    ctx->data = buf;\n    ctx->png_base = buf;\n    ctx->data_size = size;\n    ctx->bytes_left = size;\n\n    ctx->read_fn = buffer_read_fn;\n\n    ctx->state = SPNG_STATE_INPUT;\n\n    return 0;\n}\n\nint spng_set_png_stream(spng_ctx *ctx, spng_rw_fn *rw_func, void *user)\n{\n    if(ctx == NULL || rw_func == NULL) return 1;\n    if(!ctx->state) return SPNG_EBADSTATE;\n\n    /* SPNG_STATE_OUTPUT shares the same value */\n    if(ctx->state >= SPNG_STATE_INPUT) return SPNG_EBUF_SET;\n\n    if(ctx->encode_only)\n    {\n        if(ctx->out_png != NULL) return SPNG_EBUF_SET;\n\n        ctx->write_fn = rw_func;\n        ctx->write_ptr = ctx->stream_buf;\n\n        ctx->state = SPNG_STATE_OUTPUT;\n    }\n    else\n    {\n        ctx->stream_buf = spng__malloc(ctx, SPNG_READ_SIZE);\n        if(ctx->stream_buf == NULL) return SPNG_EMEM;\n\n        ctx->read_fn = rw_func;\n        ctx->data = ctx->stream_buf;\n        ctx->data_size = SPNG_READ_SIZE;\n\n        ctx->state = SPNG_STATE_INPUT;\n    }\n\n    ctx->stream_user_ptr = user;\n\n    ctx->streaming = 1;\n\n    return 0;\n}\n\nint spng_set_png_file(spng_ctx *ctx, FILE *file)\n{\n    if(file == NULL) return 1;\n\n    if(ctx->encode_only) return spng_set_png_stream(ctx, file_write_fn, file);\n\n    return spng_set_png_stream(ctx, file_read_fn, file);\n}\n\nvoid *spng_get_png_buffer(spng_ctx *ctx, size_t *len, int *error)\n{\n    int tmp = 0;\n    error = error ? error : &tmp;\n    *error = 0;\n\n    if(ctx == NULL || !len) *error = SPNG_EINVAL;\n\n    if(*error) return NULL;\n\n    if(!ctx->encode_only) *error = SPNG_ECTXTYPE;\n    else if(!ctx->state) *error = SPNG_EBADSTATE;\n    else if(!ctx->internal_buffer) *error = SPNG_EOPSTATE;\n    else if(ctx->state < SPNG_STATE_EOI) *error = SPNG_EOPSTATE;\n    else if(ctx->state != SPNG_STATE_IEND) *error = SPNG_ENOTFINAL;\n\n    if(*error) return NULL;\n\n    ctx->user_owns_out_png = 1;\n\n    *len = ctx->bytes_encoded;\n\n    return ctx->out_png;\n}\n\nint spng_set_image_limits(spng_ctx *ctx, uint32_t width, uint32_t height)\n{\n    if(ctx == NULL) return 1;\n\n    if(width > spng_u32max || height > spng_u32max) return 1;\n\n    ctx->max_width = width;\n    ctx->max_height = height;\n\n    return 0;\n}\n\nint spng_get_image_limits(spng_ctx *ctx, uint32_t *width, uint32_t *height)\n{\n    if(ctx == NULL || width == NULL || height == NULL) return 1;\n\n    *width = ctx->max_width;\n    *height = ctx->max_height;\n\n    return 0;\n}\n\nint spng_set_chunk_limits(spng_ctx *ctx, size_t chunk_size, size_t cache_limit)\n{\n    if(ctx == NULL || chunk_size > spng_u32max || chunk_size > cache_limit) return 1;\n\n    ctx->max_chunk_size = chunk_size;\n\n    ctx->chunk_cache_limit = cache_limit;\n\n    return 0;\n}\n\nint spng_get_chunk_limits(spng_ctx *ctx, size_t *chunk_size, size_t *cache_limit)\n{\n    if(ctx == NULL || chunk_size == NULL || cache_limit == NULL) return 1;\n\n    *chunk_size = ctx->max_chunk_size;\n\n    *cache_limit = ctx->chunk_cache_limit;\n\n    return 0;\n}\n\nint spng_set_crc_action(spng_ctx *ctx, int critical, int ancillary)\n{\n    if(ctx == NULL) return 1;\n    if(ctx->encode_only) return SPNG_ECTXTYPE;\n\n    if(critical > 2 || critical < 0) return 1;\n    if(ancillary > 2 || ancillary < 0) return 1;\n\n    if(critical == SPNG_CRC_DISCARD) return 1;\n\n    ctx->crc_action_critical = critical;\n    ctx->crc_action_ancillary = ancillary;\n\n    return 0;\n}\n\nint spng_set_option(spng_ctx *ctx, enum spng_option option, int value)\n{\n    if(ctx == NULL) return 1;\n    if(!ctx->state) return SPNG_EBADSTATE;\n\n    switch(option)\n    {\n        case SPNG_KEEP_UNKNOWN_CHUNKS:\n        {\n            ctx->keep_unknown = value ? 1 : 0;\n            break;\n        }\n        case SPNG_IMG_COMPRESSION_LEVEL:\n        {\n            ctx->image_options.compression_level = value;\n            break;\n        }\n        case SPNG_IMG_WINDOW_BITS:\n        {\n            ctx->image_options.window_bits = value;\n            break;\n        }\n        case SPNG_IMG_MEM_LEVEL:\n        {\n            ctx->image_options.mem_level = value;\n            break;\n        }\n        case SPNG_IMG_COMPRESSION_STRATEGY:\n        {\n            ctx->image_options.strategy = value;\n            break;\n        }\n        case SPNG_TEXT_COMPRESSION_LEVEL:\n        {\n            ctx->text_options.compression_level = value;\n            break;\n        }\n        case SPNG_TEXT_WINDOW_BITS:\n        {\n            ctx->text_options.window_bits = value;\n            break;\n        }\n        case SPNG_TEXT_MEM_LEVEL:\n        {\n            ctx->text_options.mem_level = value;\n            break;\n        }\n        case SPNG_TEXT_COMPRESSION_STRATEGY:\n        {\n            ctx->text_options.strategy = value;\n            break;\n        }\n        case SPNG_FILTER_CHOICE:\n        {\n            if(value & ~SPNG_FILTER_CHOICE_ALL) return 1;\n            ctx->encode_flags.filter_choice = value;\n            break;\n        }\n        case SPNG_CHUNK_COUNT_LIMIT:\n        {\n            if(value < 0) return 1;\n            if(value > (int)ctx->chunk_count_total) return 1;\n            ctx->chunk_count_limit = value;\n            break;\n        }\n        case SPNG_ENCODE_TO_BUFFER:\n        {\n            if(value < 0) return 1;\n            if(!ctx->encode_only) return SPNG_ECTXTYPE;\n            if(ctx->state >= SPNG_STATE_OUTPUT) return SPNG_EOPSTATE;\n\n            if(!value) break;\n\n            ctx->internal_buffer = 1;\n            ctx->state = SPNG_STATE_OUTPUT;\n\n            break;\n        }\n        default: return 1;\n    }\n\n    /* Option can no longer be overriden by the library */\n    if(option < 32) ctx->optimize_option &= ~(1 << option);\n\n    return 0;\n}\n\nint spng_get_option(spng_ctx *ctx, enum spng_option option, int *value)\n{\n    if(ctx == NULL || value == NULL) return 1;\n    if(!ctx->state) return SPNG_EBADSTATE;\n\n    switch(option)\n    {\n        case SPNG_KEEP_UNKNOWN_CHUNKS:\n        {\n            *value = ctx->keep_unknown;\n            break;\n        }\n        case SPNG_IMG_COMPRESSION_LEVEL:\n        {\n            *value = ctx->image_options.compression_level;\n            break;\n        }\n            case SPNG_IMG_WINDOW_BITS:\n        {\n            *value = ctx->image_options.window_bits;\n            break;\n        }\n        case SPNG_IMG_MEM_LEVEL:\n        {\n            *value = ctx->image_options.mem_level;\n            break;\n        }\n        case SPNG_IMG_COMPRESSION_STRATEGY:\n        {\n            *value = ctx->image_options.strategy;\n            break;\n        }\n        case SPNG_TEXT_COMPRESSION_LEVEL:\n        {\n            *value = ctx->text_options.compression_level;\n            break;\n        }\n            case SPNG_TEXT_WINDOW_BITS:\n        {\n            *value = ctx->text_options.window_bits;\n            break;\n        }\n        case SPNG_TEXT_MEM_LEVEL:\n        {\n            *value = ctx->text_options.mem_level;\n            break;\n        }\n        case SPNG_TEXT_COMPRESSION_STRATEGY:\n        {\n            *value = ctx->text_options.strategy;\n            break;\n        }\n        case SPNG_FILTER_CHOICE:\n        {\n            *value = ctx->encode_flags.filter_choice;\n            break;\n        }\n        case SPNG_CHUNK_COUNT_LIMIT:\n        {\n            *value = ctx->chunk_count_limit;\n            break;\n        }\n        case SPNG_ENCODE_TO_BUFFER:\n        {\n            if(ctx->internal_buffer) *value = 1;\n            else *value = 0;\n\n            break;\n        }\n        default: return 1;\n    }\n\n    return 0;\n}\n\nint spng_decoded_image_size(spng_ctx *ctx, int fmt, size_t *len)\n{\n    if(ctx == NULL || len == NULL) return 1;\n\n    int ret = read_chunks(ctx, 1);\n    if(ret) return ret;\n\n    ret = check_decode_fmt(&ctx->ihdr, fmt);\n    if(ret) return ret;\n\n    return calculate_image_size(&ctx->ihdr, fmt, len);\n}\n\nint spng_get_ihdr(spng_ctx *ctx, struct spng_ihdr *ihdr)\n{\n    if(ctx == NULL) return 1;\n    int ret = read_chunks(ctx, 1);\n    if(ret) return ret;\n    if(ihdr == NULL) return 1;\n\n    *ihdr = ctx->ihdr;\n\n    return 0;\n}\n\nint spng_get_plte(spng_ctx *ctx, struct spng_plte *plte)\n{\n    SPNG_GET_CHUNK_BOILERPLATE(plte);\n\n    *plte = ctx->plte;\n\n    return 0;\n}\n\nint spng_get_trns(spng_ctx *ctx, struct spng_trns *trns)\n{\n    SPNG_GET_CHUNK_BOILERPLATE(trns);\n\n    *trns = ctx->trns;\n\n    return 0;\n}\n\nint spng_get_chrm(spng_ctx *ctx, struct spng_chrm *chrm)\n{\n    SPNG_GET_CHUNK_BOILERPLATE(chrm);\n\n    chrm->white_point_x = (double)ctx->chrm_int.white_point_x / 100000.0;\n    chrm->white_point_y = (double)ctx->chrm_int.white_point_y / 100000.0;\n    chrm->red_x = (double)ctx->chrm_int.red_x / 100000.0;\n    chrm->red_y = (double)ctx->chrm_int.red_y / 100000.0;\n    chrm->blue_y = (double)ctx->chrm_int.blue_y / 100000.0;\n    chrm->blue_x = (double)ctx->chrm_int.blue_x / 100000.0;\n    chrm->green_x = (double)ctx->chrm_int.green_x / 100000.0;\n    chrm->green_y = (double)ctx->chrm_int.green_y / 100000.0;\n\n    return 0;\n}\n\nint spng_get_chrm_int(spng_ctx *ctx, struct spng_chrm_int *chrm)\n{\n    SPNG_GET_CHUNK_BOILERPLATE(chrm);\n\n    *chrm = ctx->chrm_int;\n\n    return 0;\n}\n\nint spng_get_gama(spng_ctx *ctx, double *gamma)\n{\n    double *gama = gamma;\n    SPNG_GET_CHUNK_BOILERPLATE(gama);\n\n    *gama = (double)ctx->gama / 100000.0;\n\n    return 0;\n}\n\nint spng_get_gama_int(spng_ctx *ctx, uint32_t *gama_int)\n{\n    uint32_t *gama = gama_int;\n    SPNG_GET_CHUNK_BOILERPLATE(gama);\n\n    *gama_int = ctx->gama;\n\n    return 0;\n}\n\nint spng_get_iccp(spng_ctx *ctx, struct spng_iccp *iccp)\n{\n    SPNG_GET_CHUNK_BOILERPLATE(iccp);\n\n    *iccp = ctx->iccp;\n\n    return 0;\n}\n\nint spng_get_sbit(spng_ctx *ctx, struct spng_sbit *sbit)\n{\n    SPNG_GET_CHUNK_BOILERPLATE(sbit);\n\n    *sbit = ctx->sbit;\n\n    return 0;\n}\n\nint spng_get_srgb(spng_ctx *ctx, uint8_t *rendering_intent)\n{\n    uint8_t *srgb = rendering_intent;\n    SPNG_GET_CHUNK_BOILERPLATE(srgb);\n\n    *srgb = ctx->srgb_rendering_intent;\n\n    return 0;\n}\n\nint spng_get_text(spng_ctx *ctx, struct spng_text *text, uint32_t *n_text)\n{\n    if(ctx == NULL) return 1;\n    int ret = read_chunks(ctx, 0);\n    if(ret) return ret;\n    if(!ctx->stored.text) return SPNG_ECHUNKAVAIL;\n    if(n_text == NULL) return 1;\n\n    if(text == NULL)\n    {\n        *n_text = ctx->n_text;\n        return 0;\n    }\n\n    if(*n_text < ctx->n_text) return 1;\n\n    uint32_t i;\n    for(i=0; i< ctx->n_text; i++)\n    {\n        text[i].type = ctx->text_list[i].type;\n        memcpy(&text[i].keyword, ctx->text_list[i].keyword, strlen(ctx->text_list[i].keyword) + 1);\n        text[i].compression_method = 0;\n        text[i].compression_flag = ctx->text_list[i].compression_flag;\n        text[i].language_tag = ctx->text_list[i].language_tag;\n        text[i].translated_keyword = ctx->text_list[i].translated_keyword;\n        text[i].length = ctx->text_list[i].text_length;\n        text[i].text = ctx->text_list[i].text;\n    }\n\n    return ret;\n}\n\nint spng_get_bkgd(spng_ctx *ctx, struct spng_bkgd *bkgd)\n{\n    SPNG_GET_CHUNK_BOILERPLATE(bkgd);\n\n    *bkgd = ctx->bkgd;\n\n    return 0;\n}\n\nint spng_get_hist(spng_ctx *ctx, struct spng_hist *hist)\n{\n    SPNG_GET_CHUNK_BOILERPLATE(hist);\n\n    *hist = ctx->hist;\n\n    return 0;\n}\n\nint spng_get_phys(spng_ctx *ctx, struct spng_phys *phys)\n{\n    SPNG_GET_CHUNK_BOILERPLATE(phys);\n\n    *phys = ctx->phys;\n\n    return 0;\n}\n\nint spng_get_splt(spng_ctx *ctx, struct spng_splt *splt, uint32_t *n_splt)\n{\n    if(ctx == NULL) return 1;\n    int ret = read_chunks(ctx, 0);\n    if(ret) return ret;\n    if(!ctx->stored.splt) return SPNG_ECHUNKAVAIL;\n    if(n_splt == NULL) return 1;\n\n    if(splt == NULL)\n    {\n        *n_splt = ctx->n_splt;\n        return 0;\n    }\n\n    if(*n_splt < ctx->n_splt) return 1;\n\n    memcpy(splt, ctx->splt_list, ctx->n_splt * sizeof(struct spng_splt));\n\n    return 0;\n}\n\nint spng_get_time(spng_ctx *ctx, struct spng_time *time)\n{\n    SPNG_GET_CHUNK_BOILERPLATE(time);\n\n    *time = ctx->time;\n\n    return 0;\n}\n\nint spng_get_unknown_chunks(spng_ctx *ctx, struct spng_unknown_chunk *chunks, uint32_t *n_chunks)\n{\n    if(ctx == NULL) return 1;\n    int ret = read_chunks(ctx, 0);\n    if(ret) return ret;\n    if(!ctx->stored.unknown) return SPNG_ECHUNKAVAIL;\n    if(n_chunks == NULL) return 1;\n\n    if(chunks == NULL)\n    {\n        *n_chunks = ctx->n_chunks;\n        return 0;\n    }\n\n    if(*n_chunks < ctx->n_chunks) return 1;\n\n    memcpy(chunks, ctx->chunk_list, sizeof(struct spng_unknown_chunk));\n\n    return 0;\n}\n\nint spng_get_offs(spng_ctx *ctx, struct spng_offs *offs)\n{\n    SPNG_GET_CHUNK_BOILERPLATE(offs);\n\n    *offs = ctx->offs;\n\n    return 0;\n}\n\nint spng_get_exif(spng_ctx *ctx, struct spng_exif *exif)\n{\n    SPNG_GET_CHUNK_BOILERPLATE(exif);\n\n    *exif = ctx->exif;\n\n    return 0;\n}\n\nint spng_set_ihdr(spng_ctx *ctx, struct spng_ihdr *ihdr)\n{\n    SPNG_SET_CHUNK_BOILERPLATE(ihdr);\n\n    if(ctx->stored.ihdr) return 1;\n\n    ret = check_ihdr(ihdr, ctx->max_width, ctx->max_height);\n    if(ret) return ret;\n\n    ctx->ihdr = *ihdr;\n\n    ctx->stored.ihdr = 1;\n    ctx->user.ihdr = 1;\n\n    return 0;\n}\n\nint spng_set_plte(spng_ctx *ctx, struct spng_plte *plte)\n{\n    SPNG_SET_CHUNK_BOILERPLATE(plte);\n\n    if(!ctx->stored.ihdr) return 1;\n\n    if(check_plte(plte, &ctx->ihdr)) return 1;\n\n    ctx->plte.n_entries = plte->n_entries;\n\n    memcpy(ctx->plte.entries, plte->entries, plte->n_entries * sizeof(struct spng_plte_entry));\n\n    ctx->stored.plte = 1;\n    ctx->user.plte = 1;\n\n    return 0;\n}\n\nint spng_set_trns(spng_ctx *ctx, struct spng_trns *trns)\n{\n    SPNG_SET_CHUNK_BOILERPLATE(trns);\n\n    if(!ctx->stored.ihdr) return SPNG_ENOIHDR;\n\n    if(ctx->ihdr.color_type == SPNG_COLOR_TYPE_GRAYSCALE)\n    {\n        ctx->trns.gray = trns->gray;\n    }\n    else if(ctx->ihdr.color_type == SPNG_COLOR_TYPE_TRUECOLOR)\n    {\n        ctx->trns.red = trns->red;\n        ctx->trns.green = trns->green;\n        ctx->trns.blue = trns->blue;\n    }\n    else if(ctx->ihdr.color_type == SPNG_COLOR_TYPE_INDEXED)\n    {\n        if(!ctx->stored.plte) return SPNG_ETRNS_NO_PLTE;\n        if(trns->n_type3_entries > ctx->plte.n_entries) return 1;\n\n        ctx->trns.n_type3_entries = trns->n_type3_entries;\n        memcpy(ctx->trns.type3_alpha, trns->type3_alpha, trns->n_type3_entries);\n    }\n    else return SPNG_ETRNS_COLOR_TYPE;\n\n    ctx->stored.trns = 1;\n    ctx->user.trns = 1;\n\n    return 0;\n}\n\nint spng_set_chrm(spng_ctx *ctx, struct spng_chrm *chrm)\n{\n    SPNG_SET_CHUNK_BOILERPLATE(chrm);\n\n    struct spng_chrm_int chrm_int;\n\n    chrm_int.white_point_x = (uint32_t)(chrm->white_point_x * 100000.0);\n    chrm_int.white_point_y = (uint32_t)(chrm->white_point_y * 100000.0);\n    chrm_int.red_x = (uint32_t)(chrm->red_x * 100000.0);\n    chrm_int.red_y = (uint32_t)(chrm->red_y * 100000.0);\n    chrm_int.green_x = (uint32_t)(chrm->green_x * 100000.0);\n    chrm_int.green_y = (uint32_t)(chrm->green_y * 100000.0);\n    chrm_int.blue_x = (uint32_t)(chrm->blue_x * 100000.0);\n    chrm_int.blue_y = (uint32_t)(chrm->blue_y * 100000.0);\n\n    if(check_chrm_int(&chrm_int)) return SPNG_ECHRM;\n\n    ctx->chrm_int = chrm_int;\n\n    ctx->stored.chrm = 1;\n    ctx->user.chrm = 1;\n\n    return 0;\n}\n\nint spng_set_chrm_int(spng_ctx *ctx, struct spng_chrm_int *chrm_int)\n{\n    SPNG_SET_CHUNK_BOILERPLATE(chrm_int);\n\n    if(check_chrm_int(chrm_int)) return SPNG_ECHRM;\n\n    ctx->chrm_int = *chrm_int;\n\n    ctx->stored.chrm = 1;\n    ctx->user.chrm = 1;\n\n    return 0;\n}\n\nint spng_set_gama(spng_ctx *ctx, double gamma)\n{\n    SPNG_SET_CHUNK_BOILERPLATE(ctx);\n\n    uint32_t gama = gamma * 100000.0;\n\n    if(!gama) return 1;\n    if(gama > spng_u32max) return 1;\n\n    ctx->gama = gama;\n\n    ctx->stored.gama = 1;\n    ctx->user.gama = 1;\n\n    return 0;\n}\n\nint spng_set_gama_int(spng_ctx *ctx, uint32_t gamma)\n{\n    SPNG_SET_CHUNK_BOILERPLATE(ctx);\n\n    if(!gamma) return 1;\n    if(gamma > spng_u32max) return 1;\n\n    ctx->gama = gamma;\n\n    ctx->stored.gama = 1;\n    ctx->user.gama = 1;\n\n    return 0;\n}\n\nint spng_set_iccp(spng_ctx *ctx, struct spng_iccp *iccp)\n{\n    SPNG_SET_CHUNK_BOILERPLATE(iccp);\n\n    if(check_png_keyword(iccp->profile_name)) return SPNG_EICCP_NAME;\n    if(!iccp->profile_len) return SPNG_ECHUNK_SIZE;\n    if(iccp->profile_len > spng_u32max) return SPNG_ECHUNK_STDLEN;\n\n    if(ctx->iccp.profile && !ctx->user.iccp) spng__free(ctx, ctx->iccp.profile);\n\n    ctx->iccp = *iccp;\n\n    ctx->stored.iccp = 1;\n    ctx->user.iccp = 1;\n\n    return 0;\n}\n\nint spng_set_sbit(spng_ctx *ctx, struct spng_sbit *sbit)\n{\n    SPNG_SET_CHUNK_BOILERPLATE(sbit);\n\n    if(check_sbit(sbit, &ctx->ihdr)) return 1;\n\n    if(!ctx->stored.ihdr) return 1;\n\n    ctx->sbit = *sbit;\n\n    ctx->stored.sbit = 1;\n    ctx->user.sbit = 1;\n\n    return 0;\n}\n\nint spng_set_srgb(spng_ctx *ctx, uint8_t rendering_intent)\n{\n    SPNG_SET_CHUNK_BOILERPLATE(ctx);\n\n    if(rendering_intent > 3) return 1;\n\n    ctx->srgb_rendering_intent = rendering_intent;\n\n    ctx->stored.srgb = 1;\n    ctx->user.srgb = 1;\n\n    return 0;\n}\n\nint spng_set_text(spng_ctx *ctx, struct spng_text *text, uint32_t n_text)\n{\n    if(!n_text) return 1;\n    SPNG_SET_CHUNK_BOILERPLATE(text);\n\n    uint32_t i;\n    for(i=0; i < n_text; i++)\n    {\n        if(check_png_keyword(text[i].keyword)) return SPNG_ETEXT_KEYWORD;\n        if(!text[i].length) return 1;\n        if(text[i].length > UINT_MAX) return 1;\n        if(text[i].text == NULL) return 1;\n\n        if(text[i].type == SPNG_TEXT)\n        {\n            if(ctx->strict && check_png_text(text[i].text, text[i].length)) return 1;\n        }\n        else if(text[i].type == SPNG_ZTXT)\n        {\n            if(ctx->strict && check_png_text(text[i].text, text[i].length)) return 1;\n\n            if(text[i].compression_method != 0) return SPNG_EZTXT_COMPRESSION_METHOD;\n        }\n        else if(text[i].type == SPNG_ITXT)\n        {\n            if(text[i].compression_flag > 1) return SPNG_EITXT_COMPRESSION_FLAG;\n            if(text[i].compression_method != 0) return SPNG_EITXT_COMPRESSION_METHOD;\n            if(text[i].language_tag == NULL) return SPNG_EITXT_LANG_TAG;\n            if(text[i].translated_keyword == NULL) return SPNG_EITXT_TRANSLATED_KEY;\n        }\n        else return 1;\n\n    }\n\n    struct spng_text2 *text_list = spng__calloc(ctx, sizeof(struct spng_text2), n_text);\n\n    if(!text_list) return SPNG_EMEM;\n\n    if(ctx->text_list != NULL)\n    {\n        for(i=0; i < ctx->n_text; i++)\n        {\n            if(ctx->user.text) break;\n\n            spng__free(ctx, ctx->text_list[i].keyword);\n            if(ctx->text_list[i].compression_flag) spng__free(ctx, ctx->text_list[i].text);\n        }\n        spng__free(ctx, ctx->text_list);\n    }\n\n    for(i=0; i < n_text; i++)\n    {\n        text_list[i].type = text[i].type;\n        /* Prevent issues with spng_text.keyword[80] going out of scope */\n        text_list[i].keyword = text_list[i].user_keyword_storage;\n        memcpy(text_list[i].user_keyword_storage, text[i].keyword, strlen(text[i].keyword));\n        text_list[i].text = text[i].text;\n        text_list[i].text_length = text[i].length;\n\n        if(text[i].type == SPNG_ZTXT)\n        {\n            text_list[i].compression_flag = 1;\n        }\n        else if(text[i].type == SPNG_ITXT)\n        {\n            text_list[i].compression_flag = text[i].compression_flag;\n            text_list[i].language_tag = text[i].language_tag;\n            text_list[i].translated_keyword = text[i].translated_keyword;\n        }\n    }\n\n    ctx->text_list = text_list;\n    ctx->n_text = n_text;\n\n    ctx->stored.text = 1;\n    ctx->user.text = 1;\n\n    return 0;\n}\n\nint spng_set_bkgd(spng_ctx *ctx, struct spng_bkgd *bkgd)\n{\n    SPNG_SET_CHUNK_BOILERPLATE(bkgd);\n\n    if(!ctx->stored.ihdr)  return 1;\n\n    if(ctx->ihdr.color_type == 0 || ctx->ihdr.color_type == 4)\n    {\n        ctx->bkgd.gray = bkgd->gray;\n    }\n    else if(ctx->ihdr.color_type == 2 || ctx->ihdr.color_type == 6)\n    {\n        ctx->bkgd.red = bkgd->red;\n        ctx->bkgd.green = bkgd->green;\n        ctx->bkgd.blue = bkgd->blue;\n    }\n    else if(ctx->ihdr.color_type == 3)\n    {\n        if(!ctx->stored.plte) return SPNG_EBKGD_NO_PLTE;\n        if(bkgd->plte_index >= ctx->plte.n_entries) return SPNG_EBKGD_PLTE_IDX;\n\n        ctx->bkgd.plte_index = bkgd->plte_index;\n    }\n\n    ctx->stored.bkgd = 1;\n    ctx->user.bkgd = 1;\n\n    return 0;\n}\n\nint spng_set_hist(spng_ctx *ctx, struct spng_hist *hist)\n{\n    SPNG_SET_CHUNK_BOILERPLATE(hist);\n\n    if(!ctx->stored.plte) return SPNG_EHIST_NO_PLTE;\n\n    ctx->hist = *hist;\n\n    ctx->stored.hist = 1;\n    ctx->user.hist = 1;\n\n    return 0;\n}\n\nint spng_set_phys(spng_ctx *ctx, struct spng_phys *phys)\n{\n    SPNG_SET_CHUNK_BOILERPLATE(phys);\n\n    if(check_phys(phys)) return SPNG_EPHYS;\n\n    ctx->phys = *phys;\n\n    ctx->stored.phys = 1;\n    ctx->user.phys = 1;\n\n    return 0;\n}\n\nint spng_set_splt(spng_ctx *ctx, struct spng_splt *splt, uint32_t n_splt)\n{\n    if(!n_splt) return 1;\n    SPNG_SET_CHUNK_BOILERPLATE(splt);\n\n    uint32_t i;\n    for(i=0; i < n_splt; i++)\n    {\n        if(check_png_keyword(splt[i].name)) return SPNG_ESPLT_NAME;\n        if( !(splt[i].sample_depth == 8 || splt[i].sample_depth == 16) ) return SPNG_ESPLT_DEPTH;\n    }\n\n    if(ctx->stored.splt && !ctx->user.splt)\n    {\n        for(i=0; i < ctx->n_splt; i++)\n        {\n            if(ctx->splt_list[i].entries != NULL) spng__free(ctx, ctx->splt_list[i].entries);\n        }\n        spng__free(ctx, ctx->splt_list);\n    }\n\n    ctx->splt_list = splt;\n    ctx->n_splt = n_splt;\n\n    ctx->stored.splt = 1;\n    ctx->user.splt = 1;\n\n    return 0;\n}\n\nint spng_set_time(spng_ctx *ctx, struct spng_time *time)\n{\n    SPNG_SET_CHUNK_BOILERPLATE(time);\n\n    if(check_time(time)) return SPNG_ETIME;\n\n    ctx->time = *time;\n\n    ctx->stored.time = 1;\n    ctx->user.time = 1;\n\n    return 0;\n}\n\nint spng_set_unknown_chunks(spng_ctx *ctx, struct spng_unknown_chunk *chunks, uint32_t n_chunks)\n{\n    if(!n_chunks) return 1;\n    SPNG_SET_CHUNK_BOILERPLATE(chunks);\n\n    uint32_t i;\n    for(i=0; i < n_chunks; i++)\n    {\n        if(chunks[i].length > spng_u32max) return SPNG_ECHUNK_STDLEN;\n        if(chunks[i].length && chunks[i].data == NULL) return 1;\n\n        switch(chunks[i].location)\n        {\n            case SPNG_AFTER_IHDR:\n            case SPNG_AFTER_PLTE:\n            case SPNG_AFTER_IDAT:\n            break;\n            default: return SPNG_ECHUNK_POS;\n        }\n    }\n\n    if(ctx->stored.unknown && !ctx->user.unknown)\n    {\n        for(i=0; i < ctx->n_chunks; i++)\n        {\n            spng__free(ctx, ctx->chunk_list[i].data);\n        }\n        spng__free(ctx, ctx->chunk_list);\n    }\n\n    ctx->chunk_list = chunks;\n    ctx->n_chunks = n_chunks;\n\n    ctx->stored.unknown = 1;\n    ctx->user.unknown = 1;\n\n    return 0;\n}\n\nint spng_set_offs(spng_ctx *ctx, struct spng_offs *offs)\n{\n    SPNG_SET_CHUNK_BOILERPLATE(offs);\n\n    if(check_offs(offs)) return SPNG_EOFFS;\n\n    ctx->offs = *offs;\n\n    ctx->stored.offs = 1;\n    ctx->user.offs = 1;\n\n    return 0;\n}\n\nint spng_set_exif(spng_ctx *ctx, struct spng_exif *exif)\n{\n    SPNG_SET_CHUNK_BOILERPLATE(exif);\n\n    if(check_exif(exif)) return SPNG_EEXIF;\n\n    if(ctx->exif.data != NULL && !ctx->user.exif) spng__free(ctx, ctx->exif.data);\n\n    ctx->exif = *exif;\n\n    ctx->stored.exif = 1;\n    ctx->user.exif = 1;\n\n    return 0;\n}\n\nconst char *spng_strerror(int err)\n{\n    switch(err)\n    {\n        case SPNG_IO_EOF: return \"end of stream\";\n        case SPNG_IO_ERROR: return \"stream error\";\n        case SPNG_OK: return \"success\";\n        case SPNG_EINVAL: return \"invalid argument\";\n        case SPNG_EMEM: return \"out of memory\";\n        case SPNG_EOVERFLOW: return \"arithmetic overflow\";\n        case SPNG_ESIGNATURE: return \"invalid signature\";\n        case SPNG_EWIDTH: return \"invalid image width\";\n        case SPNG_EHEIGHT: return \"invalid image height\";\n        case SPNG_EUSER_WIDTH: return \"image width exceeds user limit\";\n        case SPNG_EUSER_HEIGHT: return \"image height exceeds user limit\";\n        case SPNG_EBIT_DEPTH: return \"invalid bit depth\";\n        case SPNG_ECOLOR_TYPE: return \"invalid color type\";\n        case SPNG_ECOMPRESSION_METHOD: return \"invalid compression method\";\n        case SPNG_EFILTER_METHOD: return \"invalid filter method\";\n        case SPNG_EINTERLACE_METHOD: return \"invalid interlace method\";\n        case SPNG_EIHDR_SIZE: return \"invalid IHDR chunk size\";\n        case SPNG_ENOIHDR: return \"missing IHDR chunk\";\n        case SPNG_ECHUNK_POS: return \"invalid chunk position\";\n        case SPNG_ECHUNK_SIZE: return \"invalid chunk length\";\n        case SPNG_ECHUNK_CRC: return \"invalid chunk checksum\";\n        case SPNG_ECHUNK_TYPE: return \"invalid chunk type\";\n        case SPNG_ECHUNK_UNKNOWN_CRITICAL: return \"unknown critical chunk\";\n        case SPNG_EDUP_PLTE: return \"duplicate PLTE chunk\";\n        case SPNG_EDUP_CHRM: return \"duplicate cHRM chunk\";\n        case SPNG_EDUP_GAMA: return \"duplicate gAMA chunk\";\n        case SPNG_EDUP_ICCP: return \"duplicate iCCP chunk\";\n        case SPNG_EDUP_SBIT: return \"duplicate sBIT chunk\";\n        case SPNG_EDUP_SRGB: return \"duplicate sRGB chunk\";\n        case SPNG_EDUP_BKGD: return \"duplicate bKGD chunk\";\n        case SPNG_EDUP_HIST: return \"duplicate hIST chunk\";\n        case SPNG_EDUP_TRNS: return \"duplicate tRNS chunk\";\n        case SPNG_EDUP_PHYS: return \"duplicate pHYs chunk\";\n        case SPNG_EDUP_TIME: return \"duplicate tIME chunk\";\n        case SPNG_EDUP_OFFS: return \"duplicate oFFs chunk\";\n        case SPNG_EDUP_EXIF: return \"duplicate eXIf chunk\";\n        case SPNG_ECHRM: return \"invalid cHRM chunk\";\n        case SPNG_EPLTE_IDX: return \"invalid palette (PLTE) index\";\n        case SPNG_ETRNS_COLOR_TYPE: return \"tRNS chunk with incompatible color type\";\n        case SPNG_ETRNS_NO_PLTE: return \"missing palette (PLTE) for tRNS chunk\";\n        case SPNG_EGAMA: return \"invalid gAMA chunk\";\n        case SPNG_EICCP_NAME: return \"invalid iCCP profile name\";\n        case SPNG_EICCP_COMPRESSION_METHOD: return \"invalid iCCP compression method\";\n        case SPNG_ESBIT: return \"invalid sBIT chunk\";\n        case SPNG_ESRGB: return \"invalid sRGB chunk\";\n        case SPNG_ETEXT: return \"invalid tEXt chunk\";\n        case SPNG_ETEXT_KEYWORD: return \"invalid tEXt keyword\";\n        case SPNG_EZTXT: return \"invalid zTXt chunk\";\n        case SPNG_EZTXT_COMPRESSION_METHOD: return \"invalid zTXt compression method\";\n        case SPNG_EITXT: return \"invalid iTXt chunk\";\n        case SPNG_EITXT_COMPRESSION_FLAG: return \"invalid iTXt compression flag\";\n        case SPNG_EITXT_COMPRESSION_METHOD: return \"invalid iTXt compression method\";\n        case SPNG_EITXT_LANG_TAG: return \"invalid iTXt language tag\";\n        case SPNG_EITXT_TRANSLATED_KEY: return \"invalid iTXt translated key\";\n        case SPNG_EBKGD_NO_PLTE: return \"missing palette for bKGD chunk\";\n        case SPNG_EBKGD_PLTE_IDX: return \"invalid palette index for bKGD chunk\";\n        case SPNG_EHIST_NO_PLTE: return \"missing palette for hIST chunk\";\n        case SPNG_EPHYS: return \"invalid pHYs chunk\";\n        case SPNG_ESPLT_NAME: return \"invalid suggested palette name\";\n        case SPNG_ESPLT_DUP_NAME: return \"duplicate suggested palette (sPLT) name\";\n        case SPNG_ESPLT_DEPTH: return \"invalid suggested palette (sPLT) sample depth\";\n        case SPNG_ETIME: return \"invalid tIME chunk\";\n        case SPNG_EOFFS: return \"invalid oFFs chunk\";\n        case SPNG_EEXIF: return \"invalid eXIf chunk\";\n        case SPNG_EIDAT_TOO_SHORT: return \"IDAT stream too short\";\n        case SPNG_EIDAT_STREAM: return \"IDAT stream error\";\n        case SPNG_EZLIB: return \"zlib error\";\n        case SPNG_EFILTER: return \"invalid scanline filter\";\n        case SPNG_EBUFSIZ: return \"invalid buffer size\";\n        case SPNG_EIO: return \"i/o error\";\n        case SPNG_EOF: return \"end of file\";\n        case SPNG_EBUF_SET: return \"buffer already set\";\n        case SPNG_EBADSTATE: return \"non-recoverable state\";\n        case SPNG_EFMT: return \"invalid format\";\n        case SPNG_EFLAGS: return \"invalid flags\";\n        case SPNG_ECHUNKAVAIL: return \"chunk not available\";\n        case SPNG_ENCODE_ONLY: return \"encode only context\";\n        case SPNG_EOI: return \"reached end-of-image state\";\n        case SPNG_ENOPLTE: return \"missing PLTE for indexed image\";\n        case SPNG_ECHUNK_LIMITS: return \"reached chunk/cache limits\";\n        case SPNG_EZLIB_INIT: return \"zlib init error\";\n        case SPNG_ECHUNK_STDLEN: return \"chunk exceeds maximum standard length\";\n        case SPNG_EINTERNAL: return \"internal error\";\n        case SPNG_ECTXTYPE: return \"invalid operation for context type\";\n        case SPNG_ENOSRC: return \"source PNG not set\";\n        case SPNG_ENODST: return \"PNG output not set\";\n        case SPNG_EOPSTATE: return \"invalid operation for state\";\n        case SPNG_ENOTFINAL: return \"PNG not finalized\";\n        default: return \"unknown error\";\n    }\n}\n\nconst char *spng_version_string(void)\n{\n    return SPNG_VERSION_STRING;\n}\n\n#if defined(_MSC_VER)\n    #pragma warning(pop)\n#endif\n\n/* The following SIMD optimizations are derived from libpng source code. */\n\n/*\n* PNG Reference Library License version 2\n*\n* Copyright (c) 1995-2019 The PNG Reference Library Authors.\n* Copyright (c) 2018-2019 Cosmin Truta.\n* Copyright (c) 2000-2002, 2004, 2006-2018 Glenn Randers-Pehrson.\n* Copyright (c) 1996-1997 Andreas Dilger.\n* Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.\n*\n* The software is supplied \"as is\", without warranty of any kind,\n* express or implied, including, without limitation, the warranties\n* of merchantability, fitness for a particular purpose, title, and\n* non-infringement.  In no event shall the Copyright owners, or\n* anyone distributing the software, be liable for any damages or\n* other liability, whether in contract, tort or otherwise, arising\n* from, out of, or in connection with the software, or the use or\n* other dealings in the software, even if advised of the possibility\n* of such damage.\n*\n* Permission is hereby granted to use, copy, modify, and distribute\n* this software, or portions hereof, for any purpose, without fee,\n* subject to the following restrictions:\n*\n*  1. The origin of this software must not be misrepresented; you\n*     must not claim that you wrote the original software.  If you\n*     use this software in a product, an acknowledgment in the product\n*     documentation would be appreciated, but is not required.\n*\n*  2. Altered source versions must be plainly marked as such, and must\n*     not be misrepresented as being the original software.\n*\n*  3. This Copyright notice may not be removed or altered from any\n*     source or altered source distribution.\n*/\n\n#if defined(SPNG_X86)\n\n#ifndef SPNG_SSE\n    #define SPNG_SSE 1\n#endif\n\n#if defined(__GNUC__) && !defined(__clang__)\n    #if SPNG_SSE == 3\n        #pragma GCC target(\"ssse3\")\n    #elif SPNG_SSE == 4\n        #pragma GCC target(\"sse4.1\")\n    #else\n        #pragma GCC target(\"sse2\")\n    #endif\n#endif\n\n/* SSE2 optimised filter functions\n * Derived from filter_neon_intrinsics.c\n *\n * Copyright (c) 2018 Cosmin Truta\n * Copyright (c) 2016-2017 Glenn Randers-Pehrson\n * Written by Mike Klein and Matt Sarett\n * Derived from arm/filter_neon_intrinsics.c\n *\n * This code is derived from libpng source code.\n * For conditions of distribution and use, see the disclaimer\n * and license above.\n */\n\n#include <immintrin.h>\n#include <inttypes.h>\n#include <string.h>\n\n/* Functions in this file look at most 3 pixels (a,b,c) to predict the 4th (d).\n * They're positioned like this:\n *    prev:  c b\n *    row:   a d\n * The Sub filter predicts d=a, Avg d=(a+b)/2, and Paeth predicts d to be\n * whichever of a, b, or c is closest to p=a+b-c.\n */\n\nstatic __m128i load4(const void* p)\n{\n    int tmp;\n    memcpy(&tmp, p, sizeof(tmp));\n    return _mm_cvtsi32_si128(tmp);\n}\n\nstatic void store4(void* p, __m128i v)\n{\n    int tmp = _mm_cvtsi128_si32(v);\n    memcpy(p, &tmp, sizeof(int));\n}\n\nstatic __m128i load3(const void* p)\n{\n    uint32_t tmp = 0;\n    memcpy(&tmp, p, 3);\n    return _mm_cvtsi32_si128(tmp);\n}\n\nstatic void store3(void* p, __m128i v)\n{\n    int tmp = _mm_cvtsi128_si32(v);\n    memcpy(p, &tmp, 3);\n}\n\nstatic void defilter_sub3(size_t rowbytes, unsigned char *row)\n{\n    /* The Sub filter predicts each pixel as the previous pixel, a.\n     * There is no pixel to the left of the first pixel.  It's encoded directly.\n     * That works with our main loop if we just say that left pixel was zero.\n     */\n    size_t rb = rowbytes;\n\n    __m128i a, d = _mm_setzero_si128();\n\n    while(rb >= 4)\n    {\n        a = d; d = load4(row);\n        d = _mm_add_epi8(d, a);\n        store3(row, d);\n\n        row += 3;\n        rb  -= 3;\n    }\n\n    if(rb > 0)\n    {\n        a = d; d = load3(row);\n        d = _mm_add_epi8(d, a);\n        store3(row, d);\n    }\n}\n\nstatic void defilter_sub4(size_t rowbytes, unsigned char *row)\n{\n    /* The Sub filter predicts each pixel as the previous pixel, a.\n     * There is no pixel to the left of the first pixel.  It's encoded directly.\n     * That works with our main loop if we just say that left pixel was zero.\n     */\n    size_t rb = rowbytes+4;\n\n    __m128i a, d = _mm_setzero_si128();\n\n    while(rb > 4)\n    {\n        a = d; d = load4(row);\n        d = _mm_add_epi8(d, a);\n        store4(row, d);\n\n        row += 4;\n        rb  -= 4;\n    }\n}\n\nstatic void defilter_avg3(size_t rowbytes, unsigned char *row, const unsigned char *prev)\n{\n    /* The Avg filter predicts each pixel as the (truncated) average of a and b.\n     * There's no pixel to the left of the first pixel.  Luckily, it's\n     * predicted to be half of the pixel above it.  So again, this works\n     * perfectly with our loop if we make sure a starts at zero.\n     */\n\n    size_t rb = rowbytes;\n\n    const __m128i zero = _mm_setzero_si128();\n\n    __m128i b;\n    __m128i a, d = zero;\n\n    while(rb >= 4)\n    {\n        __m128i avg;\n               b = load4(prev);\n        a = d; d = load4(row );\n\n        /* PNG requires a truncating average, so we can't just use _mm_avg_epu8 */\n        avg = _mm_avg_epu8(a,b);\n        /* ...but we can fix it up by subtracting off 1 if it rounded up. */\n        avg = _mm_sub_epi8(avg, _mm_and_si128(_mm_xor_si128(a, b),\n                                            _mm_set1_epi8(1)));\n        d = _mm_add_epi8(d, avg);\n        store3(row, d);\n\n        prev += 3;\n        row  += 3;\n        rb   -= 3;\n    }\n\n    if(rb > 0)\n    {\n        __m128i avg;\n               b = load3(prev);\n        a = d; d = load3(row );\n\n        /* PNG requires a truncating average, so we can't just use _mm_avg_epu8 */\n        avg = _mm_avg_epu8(a, b);\n        /* ...but we can fix it up by subtracting off 1 if it rounded up. */\n        avg = _mm_sub_epi8(avg, _mm_and_si128(_mm_xor_si128(a, b),\n                                            _mm_set1_epi8(1)));\n\n        d = _mm_add_epi8(d, avg);\n        store3(row, d);\n    }\n}\n\nstatic void defilter_avg4(size_t rowbytes, unsigned char *row, const unsigned char *prev)\n{\n    /* The Avg filter predicts each pixel as the (truncated) average of a and b.\n     * There's no pixel to the left of the first pixel.  Luckily, it's\n     * predicted to be half of the pixel above it.  So again, this works\n     * perfectly with our loop if we make sure a starts at zero.\n     */\n    size_t rb = rowbytes+4;\n\n    const __m128i zero = _mm_setzero_si128();\n    __m128i    b;\n    __m128i a, d = zero;\n\n    while(rb > 4)\n    {\n        __m128i avg;\n               b = load4(prev);\n        a = d; d = load4(row );\n\n        /* PNG requires a truncating average, so we can't just use _mm_avg_epu8 */\n        avg = _mm_avg_epu8(a,b);\n        /* ...but we can fix it up by subtracting off 1 if it rounded up. */\n        avg = _mm_sub_epi8(avg, _mm_and_si128(_mm_xor_si128(a, b),\n                                            _mm_set1_epi8(1)));\n\n        d = _mm_add_epi8(d, avg);\n        store4(row, d);\n\n        prev += 4;\n        row  += 4;\n        rb   -= 4;\n    }\n}\n\n/* Returns |x| for 16-bit lanes. */\n#if (SPNG_SSE >= 3) && !defined(_MSC_VER)\n__attribute__((target(\"ssse3\")))\n#endif\nstatic __m128i abs_i16(__m128i x)\n{\n#if SPNG_SSE >= 3\n    return _mm_abs_epi16(x);\n#else\n    /* Read this all as, return x<0 ? -x : x.\n     * To negate two's complement, you flip all the bits then add 1.\n     */\n    __m128i is_negative = _mm_cmplt_epi16(x, _mm_setzero_si128());\n\n    /* Flip negative lanes. */\n    x = _mm_xor_si128(x, is_negative);\n\n    /* +1 to negative lanes, else +0. */\n    x = _mm_sub_epi16(x, is_negative);\n    return x;\n#endif\n}\n\n/* Bytewise c ? t : e. */\nstatic __m128i if_then_else(__m128i c, __m128i t, __m128i e)\n{\n#if SPNG_SSE >= 4\n    return _mm_blendv_epi8(e, t, c);\n#else\n    return _mm_or_si128(_mm_and_si128(c, t), _mm_andnot_si128(c, e));\n#endif\n}\n\nstatic void defilter_paeth3(size_t rowbytes, unsigned char *row, const unsigned char *prev)\n{\n    /* Paeth tries to predict pixel d using the pixel to the left of it, a,\n     * and two pixels from the previous row, b and c:\n     *   prev: c b\n     *   row:  a d\n     * The Paeth function predicts d to be whichever of a, b, or c is nearest to\n     * p=a+b-c.\n     *\n     * The first pixel has no left context, and so uses an Up filter, p = b.\n     * This works naturally with our main loop's p = a+b-c if we force a and c\n     * to zero.\n     * Here we zero b and d, which become c and a respectively at the start of\n     * the loop.\n     */\n    size_t rb = rowbytes;\n    const __m128i zero = _mm_setzero_si128();\n    __m128i c, b = zero,\n            a, d = zero;\n\n    while(rb >= 4)\n    {\n        /* It's easiest to do this math (particularly, deal with pc) with 16-bit\n         * intermediates.\n         */\n        __m128i pa,pb,pc,smallest,nearest;\n        c = b; b = _mm_unpacklo_epi8(load4(prev), zero);\n        a = d; d = _mm_unpacklo_epi8(load4(row ), zero);\n\n        /* (p-a) == (a+b-c - a) == (b-c) */\n\n        pa = _mm_sub_epi16(b, c);\n\n        /* (p-b) == (a+b-c - b) == (a-c) */\n        pb = _mm_sub_epi16(a, c);\n\n        /* (p-c) == (a+b-c - c) == (a+b-c-c) == (b-c)+(a-c) */\n        pc = _mm_add_epi16(pa, pb);\n\n        pa = abs_i16(pa);  /* |p-a| */\n        pb = abs_i16(pb);  /* |p-b| */\n        pc = abs_i16(pc);  /* |p-c| */\n\n        smallest = _mm_min_epi16(pc, _mm_min_epi16(pa, pb));\n\n        /* Paeth breaks ties favoring a over b over c. */\n        nearest  = if_then_else(_mm_cmpeq_epi16(smallest, pa), a,\n                            if_then_else(_mm_cmpeq_epi16(smallest, pb), b, c));\n\n        /* Note `_epi8`: we need addition to wrap modulo 255. */\n        d = _mm_add_epi8(d, nearest);\n        store3(row, _mm_packus_epi16(d, d));\n\n        prev += 3;\n        row  += 3;\n        rb   -= 3;\n    }\n\n    if(rb > 0)\n    {\n        /* It's easiest to do this math (particularly, deal with pc) with 16-bit\n         * intermediates.\n         */\n        __m128i pa, pb, pc, smallest, nearest;\n        c = b; b = _mm_unpacklo_epi8(load3(prev), zero);\n        a = d; d = _mm_unpacklo_epi8(load3(row ), zero);\n\n        /* (p-a) == (a+b-c - a) == (b-c) */\n        pa = _mm_sub_epi16(b, c);\n\n        /* (p-b) == (a+b-c - b) == (a-c) */\n        pb = _mm_sub_epi16(a, c);\n\n        /* (p-c) == (a+b-c - c) == (a+b-c-c) == (b-c)+(a-c) */\n        pc = _mm_add_epi16(pa, pb);\n\n        pa = abs_i16(pa);  /* |p-a| */\n        pb = abs_i16(pb);  /* |p-b| */\n        pc = abs_i16(pc);  /* |p-c| */\n\n        smallest = _mm_min_epi16(pc, _mm_min_epi16(pa, pb));\n\n        /* Paeth breaks ties favoring a over b over c. */\n        nearest  = if_then_else(_mm_cmpeq_epi16(smallest, pa), a,\n                            if_then_else(_mm_cmpeq_epi16(smallest, pb), b, c));\n\n        /* Note `_epi8`: we need addition to wrap modulo 255. */\n        d = _mm_add_epi8(d, nearest);\n        store3(row, _mm_packus_epi16(d, d));\n    }\n}\n\nstatic void defilter_paeth4(size_t rowbytes, unsigned char *row, const unsigned char *prev)\n{\n    /* Paeth tries to predict pixel d using the pixel to the left of it, a,\n     * and two pixels from the previous row, b and c:\n     *   prev: c b\n     *   row:  a d\n     * The Paeth function predicts d to be whichever of a, b, or c is nearest to\n     * p=a+b-c.\n     *\n     * The first pixel has no left context, and so uses an Up filter, p = b.\n     * This works naturally with our main loop's p = a+b-c if we force a and c\n     * to zero.\n     * Here we zero b and d, which become c and a respectively at the start of\n     * the loop.\n     */\n    size_t rb = rowbytes+4;\n\n    const __m128i zero = _mm_setzero_si128();\n    __m128i pa, pb, pc, smallest, nearest;\n    __m128i c, b = zero,\n            a, d = zero;\n\n    while(rb > 4)\n    {\n        /* It's easiest to do this math (particularly, deal with pc) with 16-bit\n         * intermediates.\n         */\n        c = b; b = _mm_unpacklo_epi8(load4(prev), zero);\n        a = d; d = _mm_unpacklo_epi8(load4(row ), zero);\n\n        /* (p-a) == (a+b-c - a) == (b-c) */\n        pa = _mm_sub_epi16(b, c);\n\n        /* (p-b) == (a+b-c - b) == (a-c) */\n        pb = _mm_sub_epi16(a, c);\n\n        /* (p-c) == (a+b-c - c) == (a+b-c-c) == (b-c)+(a-c) */\n        pc = _mm_add_epi16(pa, pb);\n\n        pa = abs_i16(pa);  /* |p-a| */\n        pb = abs_i16(pb);  /* |p-b| */\n        pc = abs_i16(pc);  /* |p-c| */\n\n        smallest = _mm_min_epi16(pc, _mm_min_epi16(pa, pb));\n\n        /* Paeth breaks ties favoring a over b over c. */\n        nearest  = if_then_else(_mm_cmpeq_epi16(smallest, pa), a,\n                            if_then_else(_mm_cmpeq_epi16(smallest, pb), b, c));\n\n        /* Note `_epi8`: we need addition to wrap modulo 255. */\n        d = _mm_add_epi8(d, nearest);\n        store4(row, _mm_packus_epi16(d, d));\n\n        prev += 4;\n        row  += 4;\n        rb   -= 4;\n    }\n}\n\n#endif /* SPNG_X86 */\n\n\n#if defined(SPNG_ARM)\n\n/* NEON optimised filter functions\n * Derived from filter_neon_intrinsics.c\n *\n * Copyright (c) 2018 Cosmin Truta\n * Copyright (c) 2014,2016 Glenn Randers-Pehrson\n * Written by James Yu <james.yu at linaro.org>, October 2013.\n * Based on filter_neon.S, written by Mans Rullgard, 2011.\n *\n * This code is derived from libpng source code.\n * For conditions of distribution and use, see the disclaimer\n * and license in this file.\n */\n\n#define png_aligncast(type, value) ((void*)(value))\n#define png_aligncastconst(type, value) ((const void*)(value))\n\n/* libpng row pointers are not necessarily aligned to any particular boundary,\n * however this code will only work with appropriate alignment. mips/mips_init.c\n * checks for this (and will not compile unless it is done). This code uses\n * variants of png_aligncast to avoid compiler warnings.\n */\n#define png_ptr(type,pointer) png_aligncast(type *,pointer)\n#define png_ptrc(type,pointer) png_aligncastconst(const type *,pointer)\n\n/* The following relies on a variable 'temp_pointer' being declared with type\n * 'type'.  This is written this way just to hide the GCC strict aliasing\n * warning; note that the code is safe because there never is an alias between\n * the input and output pointers.\n */\n#define png_ldr(type,pointer)\\\n   (temp_pointer = png_ptr(type,pointer), *temp_pointer)\n\n\n#if defined(_MSC_VER) && !defined(__clang__) && defined(_M_ARM64)\n    #include <arm64_neon.h>\n#else\n    #include <arm_neon.h>\n#endif\n\nstatic void defilter_sub3(size_t rowbytes, unsigned char *row)\n{\n    unsigned char *rp = row;\n    unsigned char *rp_stop = row + rowbytes;\n\n    uint8x16_t vtmp = vld1q_u8(rp);\n    uint8x8x2_t *vrpt = png_ptr(uint8x8x2_t, &vtmp);\n    uint8x8x2_t vrp = *vrpt;\n\n    uint8x8x4_t vdest;\n    vdest.val[3] = vdup_n_u8(0);\n\n    for (; rp < rp_stop;)\n    {\n        uint8x8_t vtmp1, vtmp2;\n        uint32x2_t *temp_pointer;\n\n        vtmp1 = vext_u8(vrp.val[0], vrp.val[1], 3);\n        vdest.val[0] = vadd_u8(vdest.val[3], vrp.val[0]);\n        vtmp2 = vext_u8(vrp.val[0], vrp.val[1], 6);\n        vdest.val[1] = vadd_u8(vdest.val[0], vtmp1);\n\n        vtmp1 = vext_u8(vrp.val[1], vrp.val[1], 1);\n        vdest.val[2] = vadd_u8(vdest.val[1], vtmp2);\n        vdest.val[3] = vadd_u8(vdest.val[2], vtmp1);\n\n        vtmp = vld1q_u8(rp + 12);\n        vrpt = png_ptr(uint8x8x2_t, &vtmp);\n        vrp = *vrpt;\n\n        vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[0]), 0);\n        rp += 3;\n        vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[1]), 0);\n        rp += 3;\n        vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[2]), 0);\n        rp += 3;\n        vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[3]), 0);\n        rp += 3;\n    }\n}\n\nstatic void defilter_sub4(size_t rowbytes, unsigned char *row)\n{\n    unsigned char *rp = row;\n    unsigned char *rp_stop = row + rowbytes;\n\n    uint8x8x4_t vdest;\n    vdest.val[3] = vdup_n_u8(0);\n\n    for (; rp < rp_stop; rp += 16)\n    {\n        uint32x2x4_t vtmp = vld4_u32(png_ptr(uint32_t,rp));\n        uint8x8x4_t *vrpt = png_ptr(uint8x8x4_t,&vtmp);\n        uint8x8x4_t vrp = *vrpt;\n        uint32x2x4_t *temp_pointer;\n        uint32x2x4_t vdest_val;\n\n        vdest.val[0] = vadd_u8(vdest.val[3], vrp.val[0]);\n        vdest.val[1] = vadd_u8(vdest.val[0], vrp.val[1]);\n        vdest.val[2] = vadd_u8(vdest.val[1], vrp.val[2]);\n        vdest.val[3] = vadd_u8(vdest.val[2], vrp.val[3]);\n\n        vdest_val = png_ldr(uint32x2x4_t, &vdest);\n        vst4_lane_u32(png_ptr(uint32_t,rp), vdest_val, 0);\n    }\n}\n\nstatic void defilter_avg3(size_t rowbytes, unsigned char *row, const unsigned char *prev_row)\n{\n    unsigned char *rp = row;\n    const unsigned char *pp = prev_row;\n    unsigned char *rp_stop = row + rowbytes;\n\n    uint8x16_t vtmp;\n    uint8x8x2_t *vrpt;\n    uint8x8x2_t vrp;\n    uint8x8x4_t vdest;\n    vdest.val[3] = vdup_n_u8(0);\n\n    vtmp = vld1q_u8(rp);\n    vrpt = png_ptr(uint8x8x2_t,&vtmp);\n    vrp = *vrpt;\n\n    for (; rp < rp_stop; pp += 12)\n    {\n        uint8x8_t vtmp1, vtmp2, vtmp3;\n\n        uint8x8x2_t *vppt;\n        uint8x8x2_t vpp;\n\n        uint32x2_t *temp_pointer;\n\n        vtmp = vld1q_u8(pp);\n        vppt = png_ptr(uint8x8x2_t,&vtmp);\n        vpp = *vppt;\n\n        vtmp1 = vext_u8(vrp.val[0], vrp.val[1], 3);\n        vdest.val[0] = vhadd_u8(vdest.val[3], vpp.val[0]);\n        vdest.val[0] = vadd_u8(vdest.val[0], vrp.val[0]);\n\n        vtmp2 = vext_u8(vpp.val[0], vpp.val[1], 3);\n        vtmp3 = vext_u8(vrp.val[0], vrp.val[1], 6);\n        vdest.val[1] = vhadd_u8(vdest.val[0], vtmp2);\n        vdest.val[1] = vadd_u8(vdest.val[1], vtmp1);\n\n        vtmp2 = vext_u8(vpp.val[0], vpp.val[1], 6);\n        vtmp1 = vext_u8(vrp.val[1], vrp.val[1], 1);\n\n        vtmp = vld1q_u8(rp + 12);\n        vrpt = png_ptr(uint8x8x2_t,&vtmp);\n        vrp = *vrpt;\n\n        vdest.val[2] = vhadd_u8(vdest.val[1], vtmp2);\n        vdest.val[2] = vadd_u8(vdest.val[2], vtmp3);\n\n        vtmp2 = vext_u8(vpp.val[1], vpp.val[1], 1);\n\n        vdest.val[3] = vhadd_u8(vdest.val[2], vtmp2);\n        vdest.val[3] = vadd_u8(vdest.val[3], vtmp1);\n\n        vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[0]), 0);\n        rp += 3;\n        vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[1]), 0);\n        rp += 3;\n        vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[2]), 0);\n        rp += 3;\n        vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[3]), 0);\n        rp += 3;\n    }\n}\n\nstatic void defilter_avg4(size_t rowbytes, unsigned char *row, const unsigned char *prev_row)\n{\n    unsigned char *rp = row;\n    unsigned char *rp_stop = row + rowbytes;\n    const unsigned char *pp = prev_row;\n\n    uint8x8x4_t vdest;\n    vdest.val[3] = vdup_n_u8(0);\n\n    for (; rp < rp_stop; rp += 16, pp += 16)\n    {\n        uint32x2x4_t vtmp;\n        uint8x8x4_t *vrpt, *vppt;\n        uint8x8x4_t vrp, vpp;\n        uint32x2x4_t *temp_pointer;\n        uint32x2x4_t vdest_val;\n\n        vtmp = vld4_u32(png_ptr(uint32_t,rp));\n        vrpt = png_ptr(uint8x8x4_t,&vtmp);\n        vrp = *vrpt;\n        vtmp = vld4_u32(png_ptrc(uint32_t,pp));\n        vppt = png_ptr(uint8x8x4_t,&vtmp);\n        vpp = *vppt;\n\n        vdest.val[0] = vhadd_u8(vdest.val[3], vpp.val[0]);\n        vdest.val[0] = vadd_u8(vdest.val[0], vrp.val[0]);\n        vdest.val[1] = vhadd_u8(vdest.val[0], vpp.val[1]);\n        vdest.val[1] = vadd_u8(vdest.val[1], vrp.val[1]);\n        vdest.val[2] = vhadd_u8(vdest.val[1], vpp.val[2]);\n        vdest.val[2] = vadd_u8(vdest.val[2], vrp.val[2]);\n        vdest.val[3] = vhadd_u8(vdest.val[2], vpp.val[3]);\n        vdest.val[3] = vadd_u8(vdest.val[3], vrp.val[3]);\n\n        vdest_val = png_ldr(uint32x2x4_t, &vdest);\n        vst4_lane_u32(png_ptr(uint32_t,rp), vdest_val, 0);\n    }\n}\n\nstatic uint8x8_t paeth_arm(uint8x8_t a, uint8x8_t b, uint8x8_t c)\n{\n    uint8x8_t d, e;\n    uint16x8_t p1, pa, pb, pc;\n\n    p1 = vaddl_u8(a, b); /* a + b */\n    pc = vaddl_u8(c, c); /* c * 2 */\n    pa = vabdl_u8(b, c); /* pa */\n    pb = vabdl_u8(a, c); /* pb */\n    pc = vabdq_u16(p1, pc); /* pc */\n\n    p1 = vcleq_u16(pa, pb); /* pa <= pb */\n    pa = vcleq_u16(pa, pc); /* pa <= pc */\n    pb = vcleq_u16(pb, pc); /* pb <= pc */\n\n    p1 = vandq_u16(p1, pa); /* pa <= pb && pa <= pc */\n\n    d = vmovn_u16(pb);\n    e = vmovn_u16(p1);\n\n    d = vbsl_u8(d, b, c);\n    e = vbsl_u8(e, a, d);\n\n    return e;\n}\n\nstatic void defilter_paeth3(size_t rowbytes, unsigned char *row, const unsigned char *prev_row)\n{\n    unsigned char *rp = row;\n    const unsigned char *pp = prev_row;\n    unsigned char *rp_stop = row + rowbytes;\n\n    uint8x16_t vtmp;\n    uint8x8x2_t *vrpt;\n    uint8x8x2_t vrp;\n    uint8x8_t vlast = vdup_n_u8(0);\n    uint8x8x4_t vdest;\n    vdest.val[3] = vdup_n_u8(0);\n\n    vtmp = vld1q_u8(rp);\n    vrpt = png_ptr(uint8x8x2_t,&vtmp);\n    vrp = *vrpt;\n\n    for (; rp < rp_stop; pp += 12)\n    {\n        uint8x8x2_t *vppt;\n        uint8x8x2_t vpp;\n        uint8x8_t vtmp1, vtmp2, vtmp3;\n        uint32x2_t *temp_pointer;\n\n        vtmp = vld1q_u8(pp);\n        vppt = png_ptr(uint8x8x2_t,&vtmp);\n        vpp = *vppt;\n\n        vdest.val[0] = paeth_arm(vdest.val[3], vpp.val[0], vlast);\n        vdest.val[0] = vadd_u8(vdest.val[0], vrp.val[0]);\n\n        vtmp1 = vext_u8(vrp.val[0], vrp.val[1], 3);\n        vtmp2 = vext_u8(vpp.val[0], vpp.val[1], 3);\n        vdest.val[1] = paeth_arm(vdest.val[0], vtmp2, vpp.val[0]);\n        vdest.val[1] = vadd_u8(vdest.val[1], vtmp1);\n\n        vtmp1 = vext_u8(vrp.val[0], vrp.val[1], 6);\n        vtmp3 = vext_u8(vpp.val[0], vpp.val[1], 6);\n        vdest.val[2] = paeth_arm(vdest.val[1], vtmp3, vtmp2);\n        vdest.val[2] = vadd_u8(vdest.val[2], vtmp1);\n\n        vtmp1 = vext_u8(vrp.val[1], vrp.val[1], 1);\n        vtmp2 = vext_u8(vpp.val[1], vpp.val[1], 1);\n\n        vtmp = vld1q_u8(rp + 12);\n        vrpt = png_ptr(uint8x8x2_t,&vtmp);\n        vrp = *vrpt;\n\n        vdest.val[3] = paeth_arm(vdest.val[2], vtmp2, vtmp3);\n        vdest.val[3] = vadd_u8(vdest.val[3], vtmp1);\n\n        vlast = vtmp2;\n\n        vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[0]), 0);\n        rp += 3;\n        vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[1]), 0);\n        rp += 3;\n        vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[2]), 0);\n        rp += 3;\n        vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[3]), 0);\n        rp += 3;\n    }\n}\n\nstatic void defilter_paeth4(size_t rowbytes, unsigned char *row, const unsigned char *prev_row)\n{\n    unsigned char *rp = row;\n    unsigned char *rp_stop = row + rowbytes;\n    const unsigned char *pp = prev_row;\n\n    uint8x8_t vlast = vdup_n_u8(0);\n    uint8x8x4_t vdest;\n    vdest.val[3] = vdup_n_u8(0);\n\n    for (; rp < rp_stop; rp += 16, pp += 16)\n    {\n        uint32x2x4_t vtmp;\n        uint8x8x4_t *vrpt, *vppt;\n        uint8x8x4_t vrp, vpp;\n        uint32x2x4_t *temp_pointer;\n        uint32x2x4_t vdest_val;\n\n        vtmp = vld4_u32(png_ptr(uint32_t,rp));\n        vrpt = png_ptr(uint8x8x4_t,&vtmp);\n        vrp = *vrpt;\n        vtmp = vld4_u32(png_ptrc(uint32_t,pp));\n        vppt = png_ptr(uint8x8x4_t,&vtmp);\n        vpp = *vppt;\n\n        vdest.val[0] = paeth_arm(vdest.val[3], vpp.val[0], vlast);\n        vdest.val[0] = vadd_u8(vdest.val[0], vrp.val[0]);\n        vdest.val[1] = paeth_arm(vdest.val[0], vpp.val[1], vpp.val[0]);\n        vdest.val[1] = vadd_u8(vdest.val[1], vrp.val[1]);\n        vdest.val[2] = paeth_arm(vdest.val[1], vpp.val[2], vpp.val[1]);\n        vdest.val[2] = vadd_u8(vdest.val[2], vrp.val[2]);\n        vdest.val[3] = paeth_arm(vdest.val[2], vpp.val[3], vpp.val[2]);\n        vdest.val[3] = vadd_u8(vdest.val[3], vrp.val[3]);\n\n        vlast = vpp.val[3];\n\n        vdest_val = png_ldr(uint32x2x4_t, &vdest);\n        vst4_lane_u32(png_ptr(uint32_t,rp), vdest_val, 0);\n    }\n}\n\n/* NEON optimised palette expansion functions\n * Derived from palette_neon_intrinsics.c\n *\n * Copyright (c) 2018-2019 Cosmin Truta\n * Copyright (c) 2017-2018 Arm Holdings. All rights reserved.\n * Written by Richard Townsend <Richard.Townsend@arm.com>, February 2017.\n *\n * This code is derived from libpng source code.\n * For conditions of distribution and use, see the disclaimer\n * and license in this file.\n *\n * Related: https://developer.arm.com/documentation/101964/latest/Color-palette-expansion\n *\n * The functions were refactored to iterate forward.\n *\n */\n\n/* Expands a palettized row into RGBA8. */\nstatic uint32_t expand_palette_rgba8_neon(unsigned char *row, const unsigned char *scanline, const unsigned char *plte, uint32_t width)\n{\n    const uint32_t scanline_stride = 4;\n    const uint32_t row_stride = scanline_stride * 4;\n    const uint32_t count = width / scanline_stride;\n    const uint32_t *palette = (const uint32_t*)plte;\n\n    if(!count) return 0;\n\n    uint32_t i;\n    uint32x4_t cur;\n    for(i=0; i < count; i++, scanline += scanline_stride)\n    {\n        cur = vld1q_dup_u32 (palette + scanline[0]);\n        cur = vld1q_lane_u32(palette + scanline[1], cur, 1);\n        cur = vld1q_lane_u32(palette + scanline[2], cur, 2);\n        cur = vld1q_lane_u32(palette + scanline[3], cur, 3);\n        vst1q_u32((uint32_t*)(row + i * row_stride), cur);\n    }\n\n    return count * scanline_stride;\n}\n\n/* Expands a palettized row into RGB8. */\nstatic uint32_t expand_palette_rgb8_neon(unsigned char *row, const unsigned char *scanline, const unsigned char *plte, uint32_t width)\n{\n    const uint32_t scanline_stride = 8;\n    const uint32_t row_stride = scanline_stride * 3;\n    const uint32_t count = width / scanline_stride;\n\n    if(!count) return 0;\n\n    uint32_t i;\n    uint8x8x3_t cur;\n    for(i=0; i < count; i++, scanline += scanline_stride)\n    {\n        cur = vld3_dup_u8 (plte + 3 * scanline[0]);\n        cur = vld3_lane_u8(plte + 3 * scanline[1], cur, 1);\n        cur = vld3_lane_u8(plte + 3 * scanline[2], cur, 2);\n        cur = vld3_lane_u8(plte + 3 * scanline[3], cur, 3);\n        cur = vld3_lane_u8(plte + 3 * scanline[4], cur, 4);\n        cur = vld3_lane_u8(plte + 3 * scanline[5], cur, 5);\n        cur = vld3_lane_u8(plte + 3 * scanline[6], cur, 6);\n        cur = vld3_lane_u8(plte + 3 * scanline[7], cur, 7);\n        vst3_u8(row + i * row_stride, cur);\n    }\n\n    return count * scanline_stride;\n}\n\n#endif /* SPNG_ARM */\n"
  },
  {
    "path": "jni/libspng/spng.h",
    "content": "/* SPDX-License-Identifier: BSD-2-Clause */\n#ifndef SPNG_H\n#define SPNG_H\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#if (defined(_WIN32) || defined(__CYGWIN__)) && !defined(SPNG_STATIC)\n    #if defined(SPNG__BUILD)\n        #define SPNG_API __declspec(dllexport)\n    #else\n        #define SPNG_API __declspec(dllimport)\n    #endif\n#else\n    #define SPNG_API\n#endif\n\n#if defined(_MSC_VER)\n    #define SPNG_CDECL __cdecl\n#else\n    #define SPNG_CDECL\n#endif\n\n#include <stdlib.h>\n#include <stdint.h>\n#include <stdio.h>\n\n#define SPNG_VERSION_MAJOR 0\n#define SPNG_VERSION_MINOR 7\n#define SPNG_VERSION_PATCH 3\n\nenum spng_errno\n{\n    SPNG_IO_ERROR = -2,\n    SPNG_IO_EOF = -1,\n    SPNG_OK = 0,\n    SPNG_EINVAL,\n    SPNG_EMEM,\n    SPNG_EOVERFLOW,\n    SPNG_ESIGNATURE,\n    SPNG_EWIDTH,\n    SPNG_EHEIGHT,\n    SPNG_EUSER_WIDTH,\n    SPNG_EUSER_HEIGHT,\n    SPNG_EBIT_DEPTH,\n    SPNG_ECOLOR_TYPE,\n    SPNG_ECOMPRESSION_METHOD,\n    SPNG_EFILTER_METHOD,\n    SPNG_EINTERLACE_METHOD,\n    SPNG_EIHDR_SIZE,\n    SPNG_ENOIHDR,\n    SPNG_ECHUNK_POS,\n    SPNG_ECHUNK_SIZE,\n    SPNG_ECHUNK_CRC,\n    SPNG_ECHUNK_TYPE,\n    SPNG_ECHUNK_UNKNOWN_CRITICAL,\n    SPNG_EDUP_PLTE,\n    SPNG_EDUP_CHRM,\n    SPNG_EDUP_GAMA,\n    SPNG_EDUP_ICCP,\n    SPNG_EDUP_SBIT,\n    SPNG_EDUP_SRGB,\n    SPNG_EDUP_BKGD,\n    SPNG_EDUP_HIST,\n    SPNG_EDUP_TRNS,\n    SPNG_EDUP_PHYS,\n    SPNG_EDUP_TIME,\n    SPNG_EDUP_OFFS,\n    SPNG_EDUP_EXIF,\n    SPNG_ECHRM,\n    SPNG_EPLTE_IDX,\n    SPNG_ETRNS_COLOR_TYPE,\n    SPNG_ETRNS_NO_PLTE,\n    SPNG_EGAMA,\n    SPNG_EICCP_NAME,\n    SPNG_EICCP_COMPRESSION_METHOD,\n    SPNG_ESBIT,\n    SPNG_ESRGB,\n    SPNG_ETEXT,\n    SPNG_ETEXT_KEYWORD,\n    SPNG_EZTXT,\n    SPNG_EZTXT_COMPRESSION_METHOD,\n    SPNG_EITXT,\n    SPNG_EITXT_COMPRESSION_FLAG,\n    SPNG_EITXT_COMPRESSION_METHOD,\n    SPNG_EITXT_LANG_TAG,\n    SPNG_EITXT_TRANSLATED_KEY,\n    SPNG_EBKGD_NO_PLTE,\n    SPNG_EBKGD_PLTE_IDX,\n    SPNG_EHIST_NO_PLTE,\n    SPNG_EPHYS,\n    SPNG_ESPLT_NAME,\n    SPNG_ESPLT_DUP_NAME,\n    SPNG_ESPLT_DEPTH,\n    SPNG_ETIME,\n    SPNG_EOFFS,\n    SPNG_EEXIF,\n    SPNG_EIDAT_TOO_SHORT,\n    SPNG_EIDAT_STREAM,\n    SPNG_EZLIB,\n    SPNG_EFILTER,\n    SPNG_EBUFSIZ,\n    SPNG_EIO,\n    SPNG_EOF,\n    SPNG_EBUF_SET,\n    SPNG_EBADSTATE,\n    SPNG_EFMT,\n    SPNG_EFLAGS,\n    SPNG_ECHUNKAVAIL,\n    SPNG_ENCODE_ONLY,\n    SPNG_EOI,\n    SPNG_ENOPLTE,\n    SPNG_ECHUNK_LIMITS,\n    SPNG_EZLIB_INIT,\n    SPNG_ECHUNK_STDLEN,\n    SPNG_EINTERNAL,\n    SPNG_ECTXTYPE,\n    SPNG_ENOSRC,\n    SPNG_ENODST,\n    SPNG_EOPSTATE,\n    SPNG_ENOTFINAL,\n};\n\nenum spng_text_type\n{\n    SPNG_TEXT = 1,\n    SPNG_ZTXT = 2,\n    SPNG_ITXT = 3\n};\n\nenum spng_color_type\n{\n    SPNG_COLOR_TYPE_GRAYSCALE = 0,\n    SPNG_COLOR_TYPE_TRUECOLOR = 2,\n    SPNG_COLOR_TYPE_INDEXED = 3,\n    SPNG_COLOR_TYPE_GRAYSCALE_ALPHA = 4,\n    SPNG_COLOR_TYPE_TRUECOLOR_ALPHA = 6\n};\n\nenum spng_filter\n{\n    SPNG_FILTER_NONE = 0,\n    SPNG_FILTER_SUB = 1,\n    SPNG_FILTER_UP = 2,\n    SPNG_FILTER_AVERAGE = 3,\n    SPNG_FILTER_PAETH = 4\n};\n\nenum spng_filter_choice\n{\n    SPNG_DISABLE_FILTERING = 0,\n    SPNG_FILTER_CHOICE_NONE = 8,\n    SPNG_FILTER_CHOICE_SUB = 16,\n    SPNG_FILTER_CHOICE_UP = 32,\n    SPNG_FILTER_CHOICE_AVG = 64,\n    SPNG_FILTER_CHOICE_PAETH = 128,\n    SPNG_FILTER_CHOICE_ALL = (8|16|32|64|128)\n};\n\nenum spng_interlace_method\n{\n    SPNG_INTERLACE_NONE = 0,\n    SPNG_INTERLACE_ADAM7 = 1\n};\n\n/* Channels are always in byte-order */\nenum spng_format\n{\n    SPNG_FMT_RGBA8 = 1,\n    SPNG_FMT_RGBA16 = 2,\n    SPNG_FMT_RGB8 = 4,\n\n    /* Partially implemented, see documentation */\n    SPNG_FMT_GA8 = 16,\n    SPNG_FMT_GA16 = 32,\n    SPNG_FMT_G8 = 64,\n\n    /* No conversion or scaling */\n    SPNG_FMT_PNG = 256,\n    SPNG_FMT_RAW = 512  /* big-endian (everything else is host-endian) */\n};\n\nenum spng_ctx_flags\n{\n    SPNG_CTX_IGNORE_ADLER32 = 1, /* Ignore checksum in DEFLATE streams */\n    SPNG_CTX_ENCODER = 2 /* Create an encoder context */\n};\n\nenum spng_decode_flags\n{\n    SPNG_DECODE_USE_TRNS = 1, /* Deprecated */\n    SPNG_DECODE_USE_GAMA = 2, /* Deprecated */\n    SPNG_DECODE_USE_SBIT = 8, /* Undocumented */\n\n    SPNG_DECODE_TRNS = 1, /* Apply transparency */\n    SPNG_DECODE_GAMMA = 2, /* Apply gamma correction */\n    SPNG_DECODE_PROGRESSIVE = 256 /* Initialize for progressive reads */\n};\n\nenum spng_crc_action\n{\n    /* Default for critical chunks */\n    SPNG_CRC_ERROR = 0,\n\n    /* Discard chunk, invalid for critical chunks.\n       Since v0.6.2: default for ancillary chunks */\n    SPNG_CRC_DISCARD = 1,\n\n    /* Ignore and don't calculate checksum.\n       Since v0.6.2: also ignores checksums in DEFLATE streams */\n    SPNG_CRC_USE = 2\n};\n\nenum spng_encode_flags\n{\n    SPNG_ENCODE_PROGRESSIVE = 1, /* Initialize for progressive writes */\n    SPNG_ENCODE_FINALIZE = 2, /* Finalize PNG after encoding image */\n};\n\nstruct spng_ihdr\n{\n    uint32_t width;\n    uint32_t height;\n    uint8_t bit_depth;\n    uint8_t color_type;\n    uint8_t compression_method;\n    uint8_t filter_method;\n    uint8_t interlace_method;\n};\n\nstruct spng_plte_entry\n{\n    uint8_t red;\n    uint8_t green;\n    uint8_t blue;\n\n    uint8_t alpha; /* Reserved for internal use */\n};\n\nstruct spng_plte\n{\n    uint32_t n_entries;\n    struct spng_plte_entry entries[256];\n};\n\nstruct spng_trns\n{\n    uint16_t gray;\n\n    uint16_t red;\n    uint16_t green;\n    uint16_t blue;\n\n    uint32_t n_type3_entries;\n    uint8_t type3_alpha[256];\n};\n\nstruct spng_chrm_int\n{\n    uint32_t white_point_x;\n    uint32_t white_point_y;\n    uint32_t red_x;\n    uint32_t red_y;\n    uint32_t green_x;\n    uint32_t green_y;\n    uint32_t blue_x;\n    uint32_t blue_y;\n};\n\nstruct spng_chrm\n{\n    double white_point_x;\n    double white_point_y;\n    double red_x;\n    double red_y;\n    double green_x;\n    double green_y;\n    double blue_x;\n    double blue_y;\n};\n\nstruct spng_iccp\n{\n    char profile_name[80];\n    size_t profile_len;\n    char *profile;\n};\n\nstruct spng_sbit\n{\n    uint8_t grayscale_bits;\n    uint8_t red_bits;\n    uint8_t green_bits;\n    uint8_t blue_bits;\n    uint8_t alpha_bits;\n};\n\nstruct spng_text\n{\n    char keyword[80];\n    int type;\n\n    size_t length;\n    char *text;\n\n    uint8_t compression_flag; /* iTXt only */\n    uint8_t compression_method; /* iTXt, ztXt only */\n    char *language_tag; /* iTXt only */\n    char *translated_keyword; /* iTXt only */\n};\n\nstruct spng_bkgd\n{\n    uint16_t gray; /* Only for gray/gray alpha */\n    uint16_t red;\n    uint16_t green;\n    uint16_t blue;\n    uint16_t plte_index; /* Only for indexed color */\n};\n\nstruct spng_hist\n{\n    uint16_t frequency[256];\n};\n\nstruct spng_phys\n{\n    uint32_t ppu_x, ppu_y;\n    uint8_t unit_specifier;\n};\n\nstruct spng_splt_entry\n{\n    uint16_t red;\n    uint16_t green;\n    uint16_t blue;\n    uint16_t alpha;\n    uint16_t frequency;\n};\n\nstruct spng_splt\n{\n    char name[80];\n    uint8_t sample_depth;\n    uint32_t n_entries;\n    struct spng_splt_entry *entries;\n};\n\nstruct spng_time\n{\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};\n\nstruct spng_offs\n{\n    int32_t x, y;\n    uint8_t unit_specifier;\n};\n\nstruct spng_exif\n{\n    size_t length;\n    char *data;\n};\n\nstruct spng_chunk\n{\n    size_t offset;\n    uint32_t length;\n    uint8_t type[4];\n    uint32_t crc;\n};\n\nenum spng_location\n{\n    SPNG_AFTER_IHDR = 1,\n    SPNG_AFTER_PLTE = 2,\n    SPNG_AFTER_IDAT = 8,\n};\n\nstruct spng_unknown_chunk\n{\n    uint8_t type[4];\n    size_t length;\n    void *data;\n    enum spng_location location;\n};\n\nenum spng_option\n{\n    SPNG_KEEP_UNKNOWN_CHUNKS = 1,\n\n    SPNG_IMG_COMPRESSION_LEVEL,\n    SPNG_IMG_WINDOW_BITS,\n    SPNG_IMG_MEM_LEVEL,\n    SPNG_IMG_COMPRESSION_STRATEGY,\n\n    SPNG_TEXT_COMPRESSION_LEVEL,\n    SPNG_TEXT_WINDOW_BITS,\n    SPNG_TEXT_MEM_LEVEL,\n    SPNG_TEXT_COMPRESSION_STRATEGY,\n\n    SPNG_FILTER_CHOICE,\n    SPNG_CHUNK_COUNT_LIMIT,\n    SPNG_ENCODE_TO_BUFFER,\n};\n\ntypedef void* SPNG_CDECL spng_malloc_fn(size_t size);\ntypedef void* SPNG_CDECL spng_realloc_fn(void* ptr, size_t size);\ntypedef void* SPNG_CDECL spng_calloc_fn(size_t count, size_t size);\ntypedef void SPNG_CDECL spng_free_fn(void* ptr);\n\nstruct spng_alloc\n{\n    spng_malloc_fn *malloc_fn;\n    spng_realloc_fn *realloc_fn;\n    spng_calloc_fn *calloc_fn;\n    spng_free_fn *free_fn;\n};\n\nstruct spng_row_info\n{\n    uint32_t scanline_idx;\n    uint32_t row_num; /* deinterlaced row index */\n    int pass;\n    uint8_t filter;\n};\n\ntypedef struct spng_ctx spng_ctx;\n\ntypedef int spng_read_fn(spng_ctx *ctx, void *user, void *dest, size_t length);\ntypedef int spng_write_fn(spng_ctx *ctx, void *user, void *src, size_t length);\n\ntypedef int spng_rw_fn(spng_ctx *ctx, void *user, void *dst_src, size_t length);\n\nSPNG_API spng_ctx *spng_ctx_new(int flags);\nSPNG_API spng_ctx *spng_ctx_new2(struct spng_alloc *alloc, int flags);\nSPNG_API void spng_ctx_free(spng_ctx *ctx);\n\nSPNG_API int spng_set_png_buffer(spng_ctx *ctx, const void *buf, size_t size);\nSPNG_API int spng_set_png_stream(spng_ctx *ctx, spng_rw_fn *rw_func, void *user);\nSPNG_API int spng_set_png_file(spng_ctx *ctx, FILE *file);\n\nSPNG_API void *spng_get_png_buffer(spng_ctx *ctx, size_t *len, int *error);\n\nSPNG_API int spng_set_image_limits(spng_ctx *ctx, uint32_t width, uint32_t height);\nSPNG_API int spng_get_image_limits(spng_ctx *ctx, uint32_t *width, uint32_t *height);\n\nSPNG_API int spng_set_chunk_limits(spng_ctx *ctx, size_t chunk_size, size_t cache_size);\nSPNG_API int spng_get_chunk_limits(spng_ctx *ctx, size_t *chunk_size, size_t *cache_size);\n\nSPNG_API int spng_set_crc_action(spng_ctx *ctx, int critical, int ancillary);\n\nSPNG_API int spng_set_option(spng_ctx *ctx, enum spng_option option, int value);\nSPNG_API int spng_get_option(spng_ctx *ctx, enum spng_option option, int *value);\n\nSPNG_API int spng_decoded_image_size(spng_ctx *ctx, int fmt, size_t *len);\n\n/* Decode */\nSPNG_API int spng_decode_image(spng_ctx *ctx, void *out, size_t len, int fmt, int flags);\n\n/* Progressive decode */\nSPNG_API int spng_decode_scanline(spng_ctx *ctx, void *out, size_t len);\nSPNG_API int spng_decode_row(spng_ctx *ctx, void *out, size_t len);\nSPNG_API int spng_decode_chunks(spng_ctx *ctx);\n\n/* Encode/decode */\nSPNG_API int spng_get_row_info(spng_ctx *ctx, struct spng_row_info *row_info);\n\n/* Encode */\nSPNG_API int spng_encode_image(spng_ctx *ctx, const void *img, size_t len, int fmt, int flags);\n\n/* Progressive encode */\nSPNG_API int spng_encode_scanline(spng_ctx *ctx, const void *scanline, size_t len);\nSPNG_API int spng_encode_row(spng_ctx *ctx, const void *row, size_t len);\nSPNG_API int spng_encode_chunks(spng_ctx *ctx);\n\nSPNG_API int spng_get_ihdr(spng_ctx *ctx, struct spng_ihdr *ihdr);\nSPNG_API int spng_get_plte(spng_ctx *ctx, struct spng_plte *plte);\nSPNG_API int spng_get_trns(spng_ctx *ctx, struct spng_trns *trns);\nSPNG_API int spng_get_chrm(spng_ctx *ctx, struct spng_chrm *chrm);\nSPNG_API int spng_get_chrm_int(spng_ctx *ctx, struct spng_chrm_int *chrm_int);\nSPNG_API int spng_get_gama(spng_ctx *ctx, double *gamma);\nSPNG_API int spng_get_gama_int(spng_ctx *ctx, uint32_t *gama_int);\nSPNG_API int spng_get_iccp(spng_ctx *ctx, struct spng_iccp *iccp);\nSPNG_API int spng_get_sbit(spng_ctx *ctx, struct spng_sbit *sbit);\nSPNG_API int spng_get_srgb(spng_ctx *ctx, uint8_t *rendering_intent);\nSPNG_API int spng_get_text(spng_ctx *ctx, struct spng_text *text, uint32_t *n_text);\nSPNG_API int spng_get_bkgd(spng_ctx *ctx, struct spng_bkgd *bkgd);\nSPNG_API int spng_get_hist(spng_ctx *ctx, struct spng_hist *hist);\nSPNG_API int spng_get_phys(spng_ctx *ctx, struct spng_phys *phys);\nSPNG_API int spng_get_splt(spng_ctx *ctx, struct spng_splt *splt, uint32_t *n_splt);\nSPNG_API int spng_get_time(spng_ctx *ctx, struct spng_time *time);\nSPNG_API int spng_get_unknown_chunks(spng_ctx *ctx, struct spng_unknown_chunk *chunks, uint32_t *n_chunks);\n\n/* Official extensions */\nSPNG_API int spng_get_offs(spng_ctx *ctx, struct spng_offs *offs);\nSPNG_API int spng_get_exif(spng_ctx *ctx, struct spng_exif *exif);\n\n\nSPNG_API int spng_set_ihdr(spng_ctx *ctx, struct spng_ihdr *ihdr);\nSPNG_API int spng_set_plte(spng_ctx *ctx, struct spng_plte *plte);\nSPNG_API int spng_set_trns(spng_ctx *ctx, struct spng_trns *trns);\nSPNG_API int spng_set_chrm(spng_ctx *ctx, struct spng_chrm *chrm);\nSPNG_API int spng_set_chrm_int(spng_ctx *ctx, struct spng_chrm_int *chrm_int);\nSPNG_API int spng_set_gama(spng_ctx *ctx, double gamma);\nSPNG_API int spng_set_gama_int(spng_ctx *ctx, uint32_t gamma);\nSPNG_API int spng_set_iccp(spng_ctx *ctx, struct spng_iccp *iccp);\nSPNG_API int spng_set_sbit(spng_ctx *ctx, struct spng_sbit *sbit);\nSPNG_API int spng_set_srgb(spng_ctx *ctx, uint8_t rendering_intent);\nSPNG_API int spng_set_text(spng_ctx *ctx, struct spng_text *text, uint32_t n_text);\nSPNG_API int spng_set_bkgd(spng_ctx *ctx, struct spng_bkgd *bkgd);\nSPNG_API int spng_set_hist(spng_ctx *ctx, struct spng_hist *hist);\nSPNG_API int spng_set_phys(spng_ctx *ctx, struct spng_phys *phys);\nSPNG_API int spng_set_splt(spng_ctx *ctx, struct spng_splt *splt, uint32_t n_splt);\nSPNG_API int spng_set_time(spng_ctx *ctx, struct spng_time *time);\nSPNG_API int spng_set_unknown_chunks(spng_ctx *ctx, struct spng_unknown_chunk *chunks, uint32_t n_chunks);\n\n/* Official extensions */\nSPNG_API int spng_set_offs(spng_ctx *ctx, struct spng_offs *offs);\nSPNG_API int spng_set_exif(spng_ctx *ctx, struct spng_exif *exif);\n\n\nSPNG_API const char *spng_strerror(int err);\nSPNG_API const char *spng_version_string(void);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* SPNG_H */\n"
  },
  {
    "path": "jni/lz4/lz4.c",
    "content": "/*\n   LZ4 - Fast LZ compression algorithm\n   Copyright (C) 2011-2020, Yann Collet.\n\n   BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)\n\n   Redistribution and use in source and binary forms, with or without\n   modification, are permitted provided that the following conditions are\n   met:\n\n       * Redistributions of source code must retain the above copyright\n   notice, this list of conditions and the following disclaimer.\n       * Redistributions in binary form must reproduce the above\n   copyright notice, this list of conditions and the following disclaimer\n   in the documentation and/or other materials provided with the\n   distribution.\n\n   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n   \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n   You can contact the author at :\n    - LZ4 homepage : http://www.lz4.org\n    - LZ4 source repository : https://github.com/lz4/lz4\n*/\n\n/*-************************************\n*  Tuning parameters\n**************************************/\n/*\n * LZ4_HEAPMODE :\n * Select how stateless compression functions like `LZ4_compress_default()`\n * allocate memory for their hash table,\n * in memory stack (0:default, fastest), or in memory heap (1:requires malloc()).\n */\n#ifndef LZ4_HEAPMODE\n#  define LZ4_HEAPMODE 0\n#endif\n\n/*\n * LZ4_ACCELERATION_DEFAULT :\n * Select \"acceleration\" for LZ4_compress_fast() when parameter value <= 0\n */\n#define LZ4_ACCELERATION_DEFAULT 1\n/*\n * LZ4_ACCELERATION_MAX :\n * Any \"acceleration\" value higher than this threshold\n * get treated as LZ4_ACCELERATION_MAX instead (fix #876)\n */\n#define LZ4_ACCELERATION_MAX 65537\n\n\n/*-************************************\n*  CPU Feature Detection\n**************************************/\n/* LZ4_FORCE_MEMORY_ACCESS\n * By default, access to unaligned memory is controlled by `memcpy()`, which is safe and portable.\n * Unfortunately, on some target/compiler combinations, the generated assembly is sub-optimal.\n * The below switch allow to select different access method for improved performance.\n * Method 0 (default) : use `memcpy()`. Safe and portable.\n * Method 1 : `__packed` statement. It depends on compiler extension (ie, not portable).\n *            This method is safe if your compiler supports it, and *generally* as fast or faster than `memcpy`.\n * Method 2 : direct access. This method is portable but violate C standard.\n *            It can generate buggy code on targets which assembly generation depends on alignment.\n *            But in some circumstances, it's the only known way to get the most performance (ie GCC + ARMv6)\n * See https://fastcompression.blogspot.fr/2015/08/accessing-unaligned-memory.html for details.\n * Prefer these methods in priority order (0 > 1 > 2)\n */\n#ifndef LZ4_FORCE_MEMORY_ACCESS   /* can be defined externally */\n#  if defined(__GNUC__) && \\\n  ( defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) || defined(__ARM_ARCH_6K__) \\\n  || defined(__ARM_ARCH_6Z__) || defined(__ARM_ARCH_6ZK__) || defined(__ARM_ARCH_6T2__) )\n#    define LZ4_FORCE_MEMORY_ACCESS 2\n#  elif (defined(__INTEL_COMPILER) && !defined(_WIN32)) || defined(__GNUC__)\n#    define LZ4_FORCE_MEMORY_ACCESS 1\n#  endif\n#endif\n\n/*\n * LZ4_FORCE_SW_BITCOUNT\n * Define this parameter if your target system or compiler does not support hardware bit count\n */\n#if defined(_MSC_VER) && defined(_WIN32_WCE)   /* Visual Studio for WinCE doesn't support Hardware bit count */\n#  undef  LZ4_FORCE_SW_BITCOUNT  /* avoid double def */\n#  define LZ4_FORCE_SW_BITCOUNT\n#endif\n\n\n\n/*-************************************\n*  Dependency\n**************************************/\n/*\n * LZ4_SRC_INCLUDED:\n * Amalgamation flag, whether lz4.c is included\n */\n#ifndef LZ4_SRC_INCLUDED\n#  define LZ4_SRC_INCLUDED 1\n#endif\n\n#ifndef LZ4_STATIC_LINKING_ONLY\n#define LZ4_STATIC_LINKING_ONLY\n#endif\n\n#ifndef LZ4_DISABLE_DEPRECATE_WARNINGS\n#define LZ4_DISABLE_DEPRECATE_WARNINGS /* due to LZ4_decompress_safe_withPrefix64k */\n#endif\n\n#define LZ4_STATIC_LINKING_ONLY  /* LZ4_DISTANCE_MAX */\n#include \"lz4.h\"\n/* see also \"memory routines\" below */\n\n\n/*-************************************\n*  Compiler Options\n**************************************/\n#if defined(_MSC_VER) && (_MSC_VER >= 1400)  /* Visual Studio 2005+ */\n#  include <intrin.h>               /* only present in VS2005+ */\n#  pragma warning(disable : 4127)   /* disable: C4127: conditional expression is constant */\n#  pragma warning(disable : 6237)   /* disable: C6237: conditional expression is always 0 */\n#endif  /* _MSC_VER */\n\n#ifndef LZ4_FORCE_INLINE\n#  ifdef _MSC_VER    /* Visual Studio */\n#    define LZ4_FORCE_INLINE static __forceinline\n#  else\n#    if defined (__cplusplus) || defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L   /* C99 */\n#      ifdef __GNUC__\n#        define LZ4_FORCE_INLINE static inline __attribute__((always_inline))\n#      else\n#        define LZ4_FORCE_INLINE static inline\n#      endif\n#    else\n#      define LZ4_FORCE_INLINE static\n#    endif /* __STDC_VERSION__ */\n#  endif  /* _MSC_VER */\n#endif /* LZ4_FORCE_INLINE */\n\n/* LZ4_FORCE_O2 and LZ4_FORCE_INLINE\n * gcc on ppc64le generates an unrolled SIMDized loop for LZ4_wildCopy8,\n * together with a simple 8-byte copy loop as a fall-back path.\n * However, this optimization hurts the decompression speed by >30%,\n * because the execution does not go to the optimized loop\n * for typical compressible data, and all of the preamble checks\n * before going to the fall-back path become useless overhead.\n * This optimization happens only with the -O3 flag, and -O2 generates\n * a simple 8-byte copy loop.\n * With gcc on ppc64le, all of the LZ4_decompress_* and LZ4_wildCopy8\n * functions are annotated with __attribute__((optimize(\"O2\"))),\n * and also LZ4_wildCopy8 is forcibly inlined, so that the O2 attribute\n * of LZ4_wildCopy8 does not affect the compression speed.\n */\n#if defined(__PPC64__) && defined(__LITTLE_ENDIAN__) && defined(__GNUC__) && !defined(__clang__)\n#  define LZ4_FORCE_O2  __attribute__((optimize(\"O2\")))\n#  undef LZ4_FORCE_INLINE\n#  define LZ4_FORCE_INLINE  static __inline __attribute__((optimize(\"O2\"),always_inline))\n#else\n#  define LZ4_FORCE_O2\n#endif\n\n#if (defined(__GNUC__) && (__GNUC__ >= 3)) || (defined(__INTEL_COMPILER) && (__INTEL_COMPILER >= 800)) || defined(__clang__)\n#  define expect(expr,value)    (__builtin_expect ((expr),(value)) )\n#else\n#  define expect(expr,value)    (expr)\n#endif\n\n#ifndef likely\n#define likely(expr)     expect((expr) != 0, 1)\n#endif\n#ifndef unlikely\n#define unlikely(expr)   expect((expr) != 0, 0)\n#endif\n\n/* Should the alignment test prove unreliable, for some reason,\n * it can be disabled by setting LZ4_ALIGN_TEST to 0 */\n#ifndef LZ4_ALIGN_TEST  /* can be externally provided */\n# define LZ4_ALIGN_TEST 1\n#endif\n\n\n/*-************************************\n*  Memory routines\n**************************************/\n\n/*! LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION :\n *  Disable relatively high-level LZ4/HC functions that use dynamic memory\n *  allocation functions (malloc(), calloc(), free()).\n *\n *  Note that this is a compile-time switch. And since it disables\n *  public/stable LZ4 v1 API functions, we don't recommend using this\n *  symbol to generate a library for distribution.\n *\n *  The following public functions are removed when this symbol is defined.\n *  - lz4   : LZ4_createStream, LZ4_freeStream,\n *            LZ4_createStreamDecode, LZ4_freeStreamDecode, LZ4_create (deprecated)\n *  - lz4hc : LZ4_createStreamHC, LZ4_freeStreamHC,\n *            LZ4_createHC (deprecated), LZ4_freeHC  (deprecated)\n *  - lz4frame, lz4file : All LZ4F_* functions\n */\n#if defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION)\n#  define ALLOC(s)          lz4_error_memory_allocation_is_disabled\n#  define ALLOC_AND_ZERO(s) lz4_error_memory_allocation_is_disabled\n#  define FREEMEM(p)        lz4_error_memory_allocation_is_disabled\n#elif defined(LZ4_USER_MEMORY_FUNCTIONS)\n/* memory management functions can be customized by user project.\n * Below functions must exist somewhere in the Project\n * and be available at link time */\nvoid* LZ4_malloc(size_t s);\nvoid* LZ4_calloc(size_t n, size_t s);\nvoid  LZ4_free(void* p);\n# define ALLOC(s)          LZ4_malloc(s)\n# define ALLOC_AND_ZERO(s) LZ4_calloc(1,s)\n# define FREEMEM(p)        LZ4_free(p)\n#else\n# include <stdlib.h>   /* malloc, calloc, free */\n# define ALLOC(s)          malloc(s)\n# define ALLOC_AND_ZERO(s) calloc(1,s)\n# define FREEMEM(p)        free(p)\n#endif\n\n#if ! LZ4_FREESTANDING\n#  include <string.h>   /* memset, memcpy */\n#endif\n#if !defined(LZ4_memset)\n#  define LZ4_memset(p,v,s) memset((p),(v),(s))\n#endif\n#define MEM_INIT(p,v,s)   LZ4_memset((p),(v),(s))\n\n\n/*-************************************\n*  Common Constants\n**************************************/\n#define MINMATCH 4\n\n#define WILDCOPYLENGTH 8\n#define LASTLITERALS   5   /* see ../doc/lz4_Block_format.md#parsing-restrictions */\n#define MFLIMIT       12   /* see ../doc/lz4_Block_format.md#parsing-restrictions */\n#define MATCH_SAFEGUARD_DISTANCE  ((2*WILDCOPYLENGTH) - MINMATCH)   /* ensure it's possible to write 2 x wildcopyLength without overflowing output buffer */\n#define FASTLOOP_SAFE_DISTANCE 64\nstatic const int LZ4_minLength = (MFLIMIT+1);\n\n#define KB *(1 <<10)\n#define MB *(1 <<20)\n#define GB *(1U<<30)\n\n#define LZ4_DISTANCE_ABSOLUTE_MAX 65535\n#if (LZ4_DISTANCE_MAX > LZ4_DISTANCE_ABSOLUTE_MAX)   /* max supported by LZ4 format */\n#  error \"LZ4_DISTANCE_MAX is too big : must be <= 65535\"\n#endif\n\n#define ML_BITS  4\n#define ML_MASK  ((1U<<ML_BITS)-1)\n#define RUN_BITS (8-ML_BITS)\n#define RUN_MASK ((1U<<RUN_BITS)-1)\n\n\n/*-************************************\n*  Error detection\n**************************************/\n#if defined(LZ4_DEBUG) && (LZ4_DEBUG>=1)\n#  include <assert.h>\n#else\n#  ifndef assert\n#    define assert(condition) ((void)0)\n#  endif\n#endif\n\n#define LZ4_STATIC_ASSERT(c)   { enum { LZ4_static_assert = 1/(int)(!!(c)) }; }   /* use after variable declarations */\n\n#if defined(LZ4_DEBUG) && (LZ4_DEBUG>=2)\n#  include <stdio.h>\n   static int g_debuglog_enable = 1;\n#  define DEBUGLOG(l, ...) {                          \\\n        if ((g_debuglog_enable) && (l<=LZ4_DEBUG)) {  \\\n            fprintf(stderr, __FILE__  \" %i: \", __LINE__); \\\n            fprintf(stderr, __VA_ARGS__);             \\\n            fprintf(stderr, \" \\n\");                   \\\n    }   }\n#else\n#  define DEBUGLOG(l, ...) {}    /* disabled */\n#endif\n\nstatic int LZ4_isAligned(const void* ptr, size_t alignment)\n{\n    return ((size_t)ptr & (alignment -1)) == 0;\n}\n\n\n/*-************************************\n*  Types\n**************************************/\n#include <limits.h>\n#if defined(__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */)\n# include <stdint.h>\n  typedef  uint8_t BYTE;\n  typedef uint16_t U16;\n  typedef uint32_t U32;\n  typedef  int32_t S32;\n  typedef uint64_t U64;\n  typedef uintptr_t uptrval;\n#else\n# if UINT_MAX != 4294967295UL\n#   error \"LZ4 code (when not C++ or C99) assumes that sizeof(int) == 4\"\n# endif\n  typedef unsigned char       BYTE;\n  typedef unsigned short      U16;\n  typedef unsigned int        U32;\n  typedef   signed int        S32;\n  typedef unsigned long long  U64;\n  typedef size_t              uptrval;   /* generally true, except OpenVMS-64 */\n#endif\n\n#if defined(__x86_64__)\n  typedef U64    reg_t;   /* 64-bits in x32 mode */\n#else\n  typedef size_t reg_t;   /* 32-bits in x32 mode */\n#endif\n\ntypedef enum {\n    notLimited = 0,\n    limitedOutput = 1,\n    fillOutput = 2\n} limitedOutput_directive;\n\n\n/*-************************************\n*  Reading and writing into memory\n**************************************/\n\n/**\n * LZ4 relies on memcpy with a constant size being inlined. In freestanding\n * environments, the compiler can't assume the implementation of memcpy() is\n * standard compliant, so it can't apply its specialized memcpy() inlining\n * logic. When possible, use __builtin_memcpy() to tell the compiler to analyze\n * memcpy() as if it were standard compliant, so it can inline it in freestanding\n * environments. This is needed when decompressing the Linux Kernel, for example.\n */\n#if !defined(LZ4_memcpy)\n#  if defined(__GNUC__) && (__GNUC__ >= 4)\n#    define LZ4_memcpy(dst, src, size) __builtin_memcpy(dst, src, size)\n#  else\n#    define LZ4_memcpy(dst, src, size) memcpy(dst, src, size)\n#  endif\n#endif\n\n#if !defined(LZ4_memmove)\n#  if defined(__GNUC__) && (__GNUC__ >= 4)\n#    define LZ4_memmove __builtin_memmove\n#  else\n#    define LZ4_memmove memmove\n#  endif\n#endif\n\nstatic unsigned LZ4_isLittleEndian(void)\n{\n    const union { U32 u; BYTE c[4]; } one = { 1 };   /* don't use static : performance detrimental */\n    return one.c[0];\n}\n\n\n#if defined(LZ4_FORCE_MEMORY_ACCESS) && (LZ4_FORCE_MEMORY_ACCESS==2)\n/* lie to the compiler about data alignment; use with caution */\n\nstatic U16 LZ4_read16(const void* memPtr) { return *(const U16*) memPtr; }\nstatic U32 LZ4_read32(const void* memPtr) { return *(const U32*) memPtr; }\nstatic reg_t LZ4_read_ARCH(const void* memPtr) { return *(const reg_t*) memPtr; }\n\nstatic void LZ4_write16(void* memPtr, U16 value) { *(U16*)memPtr = value; }\nstatic void LZ4_write32(void* memPtr, U32 value) { *(U32*)memPtr = value; }\n\n#elif defined(LZ4_FORCE_MEMORY_ACCESS) && (LZ4_FORCE_MEMORY_ACCESS==1)\n\n/* __pack instructions are safer, but compiler specific, hence potentially problematic for some compilers */\n/* currently only defined for gcc and icc */\ntypedef struct { U16 u16; } __attribute__((packed)) LZ4_unalign16;\ntypedef struct { U32 u32; } __attribute__((packed)) LZ4_unalign32;\ntypedef struct { reg_t uArch; } __attribute__((packed)) LZ4_unalignST;\n\nstatic U16 LZ4_read16(const void* ptr) { return ((const LZ4_unalign16*)ptr)->u16; }\nstatic U32 LZ4_read32(const void* ptr) { return ((const LZ4_unalign32*)ptr)->u32; }\nstatic reg_t LZ4_read_ARCH(const void* ptr) { return ((const LZ4_unalignST*)ptr)->uArch; }\n\nstatic void LZ4_write16(void* memPtr, U16 value) { ((LZ4_unalign16*)memPtr)->u16 = value; }\nstatic void LZ4_write32(void* memPtr, U32 value) { ((LZ4_unalign32*)memPtr)->u32 = value; }\n\n#else  /* safe and portable access using memcpy() */\n\nstatic U16 LZ4_read16(const void* memPtr)\n{\n    U16 val; LZ4_memcpy(&val, memPtr, sizeof(val)); return val;\n}\n\nstatic U32 LZ4_read32(const void* memPtr)\n{\n    U32 val; LZ4_memcpy(&val, memPtr, sizeof(val)); return val;\n}\n\nstatic reg_t LZ4_read_ARCH(const void* memPtr)\n{\n    reg_t val; LZ4_memcpy(&val, memPtr, sizeof(val)); return val;\n}\n\nstatic void LZ4_write16(void* memPtr, U16 value)\n{\n    LZ4_memcpy(memPtr, &value, sizeof(value));\n}\n\nstatic void LZ4_write32(void* memPtr, U32 value)\n{\n    LZ4_memcpy(memPtr, &value, sizeof(value));\n}\n\n#endif /* LZ4_FORCE_MEMORY_ACCESS */\n\n\nstatic U16 LZ4_readLE16(const void* memPtr)\n{\n    if (LZ4_isLittleEndian()) {\n        return LZ4_read16(memPtr);\n    } else {\n        const BYTE* p = (const BYTE*)memPtr;\n        return (U16)((U16)p[0] + (p[1]<<8));\n    }\n}\n\nstatic void LZ4_writeLE16(void* memPtr, U16 value)\n{\n    if (LZ4_isLittleEndian()) {\n        LZ4_write16(memPtr, value);\n    } else {\n        BYTE* p = (BYTE*)memPtr;\n        p[0] = (BYTE) value;\n        p[1] = (BYTE)(value>>8);\n    }\n}\n\n/* customized variant of memcpy, which can overwrite up to 8 bytes beyond dstEnd */\nLZ4_FORCE_INLINE\nvoid LZ4_wildCopy8(void* dstPtr, const void* srcPtr, void* dstEnd)\n{\n    BYTE* d = (BYTE*)dstPtr;\n    const BYTE* s = (const BYTE*)srcPtr;\n    BYTE* const e = (BYTE*)dstEnd;\n\n    do { LZ4_memcpy(d,s,8); d+=8; s+=8; } while (d<e);\n}\n\nstatic const unsigned inc32table[8] = {0, 1, 2,  1,  0,  4, 4, 4};\nstatic const int      dec64table[8] = {0, 0, 0, -1, -4,  1, 2, 3};\n\n\n#ifndef LZ4_FAST_DEC_LOOP\n#  if defined __i386__ || defined _M_IX86 || defined __x86_64__ || defined _M_X64\n#    define LZ4_FAST_DEC_LOOP 1\n#  elif defined(__aarch64__) && defined(__APPLE__)\n#    define LZ4_FAST_DEC_LOOP 1\n#  elif defined(__aarch64__) && !defined(__clang__)\n     /* On non-Apple aarch64, we disable this optimization for clang because\n      * on certain mobile chipsets, performance is reduced with clang. For\n      * more information refer to https://github.com/lz4/lz4/pull/707 */\n#    define LZ4_FAST_DEC_LOOP 1\n#  else\n#    define LZ4_FAST_DEC_LOOP 0\n#  endif\n#endif\n\n#if LZ4_FAST_DEC_LOOP\n\nLZ4_FORCE_INLINE void\nLZ4_memcpy_using_offset_base(BYTE* dstPtr, const BYTE* srcPtr, BYTE* dstEnd, const size_t offset)\n{\n    assert(srcPtr + offset == dstPtr);\n    if (offset < 8) {\n        LZ4_write32(dstPtr, 0);   /* silence an msan warning when offset==0 */\n        dstPtr[0] = srcPtr[0];\n        dstPtr[1] = srcPtr[1];\n        dstPtr[2] = srcPtr[2];\n        dstPtr[3] = srcPtr[3];\n        srcPtr += inc32table[offset];\n        LZ4_memcpy(dstPtr+4, srcPtr, 4);\n        srcPtr -= dec64table[offset];\n        dstPtr += 8;\n    } else {\n        LZ4_memcpy(dstPtr, srcPtr, 8);\n        dstPtr += 8;\n        srcPtr += 8;\n    }\n\n    LZ4_wildCopy8(dstPtr, srcPtr, dstEnd);\n}\n\n/* customized variant of memcpy, which can overwrite up to 32 bytes beyond dstEnd\n * this version copies two times 16 bytes (instead of one time 32 bytes)\n * because it must be compatible with offsets >= 16. */\nLZ4_FORCE_INLINE void\nLZ4_wildCopy32(void* dstPtr, const void* srcPtr, void* dstEnd)\n{\n    BYTE* d = (BYTE*)dstPtr;\n    const BYTE* s = (const BYTE*)srcPtr;\n    BYTE* const e = (BYTE*)dstEnd;\n\n    do { LZ4_memcpy(d,s,16); LZ4_memcpy(d+16,s+16,16); d+=32; s+=32; } while (d<e);\n}\n\n/* LZ4_memcpy_using_offset()  presumes :\n * - dstEnd >= dstPtr + MINMATCH\n * - there is at least 8 bytes available to write after dstEnd */\nLZ4_FORCE_INLINE void\nLZ4_memcpy_using_offset(BYTE* dstPtr, const BYTE* srcPtr, BYTE* dstEnd, const size_t offset)\n{\n    BYTE v[8];\n\n    assert(dstEnd >= dstPtr + MINMATCH);\n\n    switch(offset) {\n    case 1:\n        MEM_INIT(v, *srcPtr, 8);\n        break;\n    case 2:\n        LZ4_memcpy(v, srcPtr, 2);\n        LZ4_memcpy(&v[2], srcPtr, 2);\n#if defined(_MSC_VER) && (_MSC_VER <= 1933) /* MSVC 2022 ver 17.3 or earlier */\n#  pragma warning(push)\n#  pragma warning(disable : 6385) /* warning C6385: Reading invalid data from 'v'. */\n#endif\n        LZ4_memcpy(&v[4], v, 4);\n#if defined(_MSC_VER) && (_MSC_VER <= 1933) /* MSVC 2022 ver 17.3 or earlier */\n#  pragma warning(pop)\n#endif\n        break;\n    case 4:\n        LZ4_memcpy(v, srcPtr, 4);\n        LZ4_memcpy(&v[4], srcPtr, 4);\n        break;\n    default:\n        LZ4_memcpy_using_offset_base(dstPtr, srcPtr, dstEnd, offset);\n        return;\n    }\n\n    LZ4_memcpy(dstPtr, v, 8);\n    dstPtr += 8;\n    while (dstPtr < dstEnd) {\n        LZ4_memcpy(dstPtr, v, 8);\n        dstPtr += 8;\n    }\n}\n#endif\n\n\n/*-************************************\n*  Common functions\n**************************************/\nstatic unsigned LZ4_NbCommonBytes (reg_t val)\n{\n    assert(val != 0);\n    if (LZ4_isLittleEndian()) {\n        if (sizeof(val) == 8) {\n#       if defined(_MSC_VER) && (_MSC_VER >= 1800) && (defined(_M_AMD64) && !defined(_M_ARM64EC)) && !defined(LZ4_FORCE_SW_BITCOUNT)\n/*-*************************************************************************************************\n* ARM64EC is a Microsoft-designed ARM64 ABI compatible with AMD64 applications on ARM64 Windows 11.\n* The ARM64EC ABI does not support AVX/AVX2/AVX512 instructions, nor their relevant intrinsics\n* including _tzcnt_u64. Therefore, we need to neuter the _tzcnt_u64 code path for ARM64EC.\n****************************************************************************************************/\n#         if defined(__clang__) && (__clang_major__ < 10)\n            /* Avoid undefined clang-cl intrinsics issue.\n             * See https://github.com/lz4/lz4/pull/1017 for details. */\n            return (unsigned)__builtin_ia32_tzcnt_u64(val) >> 3;\n#         else\n            /* x64 CPUS without BMI support interpret `TZCNT` as `REP BSF` */\n            return (unsigned)_tzcnt_u64(val) >> 3;\n#         endif\n#       elif defined(_MSC_VER) && defined(_WIN64) && !defined(LZ4_FORCE_SW_BITCOUNT)\n            unsigned long r = 0;\n            _BitScanForward64(&r, (U64)val);\n            return (unsigned)r >> 3;\n#       elif (defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 3) || \\\n                            ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 4))))) && \\\n                                        !defined(LZ4_FORCE_SW_BITCOUNT)\n            return (unsigned)__builtin_ctzll((U64)val) >> 3;\n#       else\n            const U64 m = 0x0101010101010101ULL;\n            val ^= val - 1;\n            return (unsigned)(((U64)((val & (m - 1)) * m)) >> 56);\n#       endif\n        } else /* 32 bits */ {\n#       if defined(_MSC_VER) && (_MSC_VER >= 1400) && !defined(LZ4_FORCE_SW_BITCOUNT)\n            unsigned long r;\n            _BitScanForward(&r, (U32)val);\n            return (unsigned)r >> 3;\n#       elif (defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 3) || \\\n                            ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 4))))) && \\\n                        !defined(__TINYC__) && !defined(LZ4_FORCE_SW_BITCOUNT)\n            return (unsigned)__builtin_ctz((U32)val) >> 3;\n#       else\n            const U32 m = 0x01010101;\n            return (unsigned)((((val - 1) ^ val) & (m - 1)) * m) >> 24;\n#       endif\n        }\n    } else   /* Big Endian CPU */ {\n        if (sizeof(val)==8) {\n#       if (defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 3) || \\\n                            ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 4))))) && \\\n                        !defined(__TINYC__) && !defined(LZ4_FORCE_SW_BITCOUNT)\n            return (unsigned)__builtin_clzll((U64)val) >> 3;\n#       else\n#if 1\n            /* this method is probably faster,\n             * but adds a 128 bytes lookup table */\n            static const unsigned char ctz7_tab[128] = {\n                7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,\n                4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,\n                5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,\n                4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,\n                6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,\n                4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,\n                5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,\n                4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,\n            };\n            U64 const mask = 0x0101010101010101ULL;\n            U64 const t = (((val >> 8) - mask) | val) & mask;\n            return ctz7_tab[(t * 0x0080402010080402ULL) >> 57];\n#else\n            /* this method doesn't consume memory space like the previous one,\n             * but it contains several branches,\n             * that may end up slowing execution */\n            static const U32 by32 = sizeof(val)*4;  /* 32 on 64 bits (goal), 16 on 32 bits.\n            Just to avoid some static analyzer complaining about shift by 32 on 32-bits target.\n            Note that this code path is never triggered in 32-bits mode. */\n            unsigned r;\n            if (!(val>>by32)) { r=4; } else { r=0; val>>=by32; }\n            if (!(val>>16)) { r+=2; val>>=8; } else { val>>=24; }\n            r += (!val);\n            return r;\n#endif\n#       endif\n        } else /* 32 bits */ {\n#       if (defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 3) || \\\n                            ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 4))))) && \\\n                                        !defined(LZ4_FORCE_SW_BITCOUNT)\n            return (unsigned)__builtin_clz((U32)val) >> 3;\n#       else\n            val >>= 8;\n            val = ((((val + 0x00FFFF00) | 0x00FFFFFF) + val) |\n              (val + 0x00FF0000)) >> 24;\n            return (unsigned)val ^ 3;\n#       endif\n        }\n    }\n}\n\n\n#define STEPSIZE sizeof(reg_t)\nLZ4_FORCE_INLINE\nunsigned LZ4_count(const BYTE* pIn, const BYTE* pMatch, const BYTE* pInLimit)\n{\n    const BYTE* const pStart = pIn;\n\n    if (likely(pIn < pInLimit-(STEPSIZE-1))) {\n        reg_t const diff = LZ4_read_ARCH(pMatch) ^ LZ4_read_ARCH(pIn);\n        if (!diff) {\n            pIn+=STEPSIZE; pMatch+=STEPSIZE;\n        } else {\n            return LZ4_NbCommonBytes(diff);\n    }   }\n\n    while (likely(pIn < pInLimit-(STEPSIZE-1))) {\n        reg_t const diff = LZ4_read_ARCH(pMatch) ^ LZ4_read_ARCH(pIn);\n        if (!diff) { pIn+=STEPSIZE; pMatch+=STEPSIZE; continue; }\n        pIn += LZ4_NbCommonBytes(diff);\n        return (unsigned)(pIn - pStart);\n    }\n\n    if ((STEPSIZE==8) && (pIn<(pInLimit-3)) && (LZ4_read32(pMatch) == LZ4_read32(pIn))) { pIn+=4; pMatch+=4; }\n    if ((pIn<(pInLimit-1)) && (LZ4_read16(pMatch) == LZ4_read16(pIn))) { pIn+=2; pMatch+=2; }\n    if ((pIn<pInLimit) && (*pMatch == *pIn)) pIn++;\n    return (unsigned)(pIn - pStart);\n}\n\n\n#ifndef LZ4_COMMONDEFS_ONLY\n/*-************************************\n*  Local Constants\n**************************************/\nstatic const int LZ4_64Klimit = ((64 KB) + (MFLIMIT-1));\nstatic const U32 LZ4_skipTrigger = 6;  /* Increase this value ==> compression run slower on incompressible data */\n\n\n/*-************************************\n*  Local Structures and types\n**************************************/\ntypedef enum { clearedTable = 0, byPtr, byU32, byU16 } tableType_t;\n\n/**\n * This enum distinguishes several different modes of accessing previous\n * content in the stream.\n *\n * - noDict        : There is no preceding content.\n * - withPrefix64k : Table entries up to ctx->dictSize before the current blob\n *                   blob being compressed are valid and refer to the preceding\n *                   content (of length ctx->dictSize), which is available\n *                   contiguously preceding in memory the content currently\n *                   being compressed.\n * - usingExtDict  : Like withPrefix64k, but the preceding content is somewhere\n *                   else in memory, starting at ctx->dictionary with length\n *                   ctx->dictSize.\n * - usingDictCtx  : Everything concerning the preceding content is\n *                   in a separate context, pointed to by ctx->dictCtx.\n *                   ctx->dictionary, ctx->dictSize, and table entries\n *                   in the current context that refer to positions\n *                   preceding the beginning of the current compression are\n *                   ignored. Instead, ctx->dictCtx->dictionary and ctx->dictCtx\n *                   ->dictSize describe the location and size of the preceding\n *                   content, and matches are found by looking in the ctx\n *                   ->dictCtx->hashTable.\n */\ntypedef enum { noDict = 0, withPrefix64k, usingExtDict, usingDictCtx } dict_directive;\ntypedef enum { noDictIssue = 0, dictSmall } dictIssue_directive;\n\n\n/*-************************************\n*  Local Utils\n**************************************/\nint LZ4_versionNumber (void) { return LZ4_VERSION_NUMBER; }\nconst char* LZ4_versionString(void) { return LZ4_VERSION_STRING; }\nint LZ4_compressBound(int isize)  { return LZ4_COMPRESSBOUND(isize); }\nint LZ4_sizeofState(void) { return sizeof(LZ4_stream_t); }\n\n\n/*-****************************************\n*  Internal Definitions, used only in Tests\n*******************************************/\n#if defined (__cplusplus)\nextern \"C\" {\n#endif\n\nint LZ4_compress_forceExtDict (LZ4_stream_t* LZ4_dict, const char* source, char* dest, int srcSize);\n\nint LZ4_decompress_safe_forceExtDict(const char* source, char* dest,\n                                     int compressedSize, int maxOutputSize,\n                                     const void* dictStart, size_t dictSize);\nint LZ4_decompress_safe_partial_forceExtDict(const char* source, char* dest,\n                                     int compressedSize, int targetOutputSize, int dstCapacity,\n                                     const void* dictStart, size_t dictSize);\n#if defined (__cplusplus)\n}\n#endif\n\n/*-******************************\n*  Compression functions\n********************************/\nLZ4_FORCE_INLINE U32 LZ4_hash4(U32 sequence, tableType_t const tableType)\n{\n    if (tableType == byU16)\n        return ((sequence * 2654435761U) >> ((MINMATCH*8)-(LZ4_HASHLOG+1)));\n    else\n        return ((sequence * 2654435761U) >> ((MINMATCH*8)-LZ4_HASHLOG));\n}\n\nLZ4_FORCE_INLINE U32 LZ4_hash5(U64 sequence, tableType_t const tableType)\n{\n    const U32 hashLog = (tableType == byU16) ? LZ4_HASHLOG+1 : LZ4_HASHLOG;\n    if (LZ4_isLittleEndian()) {\n        const U64 prime5bytes = 889523592379ULL;\n        return (U32)(((sequence << 24) * prime5bytes) >> (64 - hashLog));\n    } else {\n        const U64 prime8bytes = 11400714785074694791ULL;\n        return (U32)(((sequence >> 24) * prime8bytes) >> (64 - hashLog));\n    }\n}\n\nLZ4_FORCE_INLINE U32 LZ4_hashPosition(const void* const p, tableType_t const tableType)\n{\n    if ((sizeof(reg_t)==8) && (tableType != byU16)) return LZ4_hash5(LZ4_read_ARCH(p), tableType);\n    return LZ4_hash4(LZ4_read32(p), tableType);\n}\n\nLZ4_FORCE_INLINE void LZ4_clearHash(U32 h, void* tableBase, tableType_t const tableType)\n{\n    switch (tableType)\n    {\n    default: /* fallthrough */\n    case clearedTable: { /* illegal! */ assert(0); return; }\n    case byPtr: { const BYTE** hashTable = (const BYTE**)tableBase; hashTable[h] = NULL; return; }\n    case byU32: { U32* hashTable = (U32*) tableBase; hashTable[h] = 0; return; }\n    case byU16: { U16* hashTable = (U16*) tableBase; hashTable[h] = 0; return; }\n    }\n}\n\nLZ4_FORCE_INLINE void LZ4_putIndexOnHash(U32 idx, U32 h, void* tableBase, tableType_t const tableType)\n{\n    switch (tableType)\n    {\n    default: /* fallthrough */\n    case clearedTable: /* fallthrough */\n    case byPtr: { /* illegal! */ assert(0); return; }\n    case byU32: { U32* hashTable = (U32*) tableBase; hashTable[h] = idx; return; }\n    case byU16: { U16* hashTable = (U16*) tableBase; assert(idx < 65536); hashTable[h] = (U16)idx; return; }\n    }\n}\n\n/* LZ4_putPosition*() : only used in byPtr mode */\nLZ4_FORCE_INLINE void LZ4_putPositionOnHash(const BYTE* p, U32 h,\n                                  void* tableBase, tableType_t const tableType)\n{\n    const BYTE** const hashTable = (const BYTE**)tableBase;\n    assert(tableType == byPtr); (void)tableType;\n    hashTable[h] = p;\n}\n\nLZ4_FORCE_INLINE void LZ4_putPosition(const BYTE* p, void* tableBase, tableType_t tableType)\n{\n    U32 const h = LZ4_hashPosition(p, tableType);\n    LZ4_putPositionOnHash(p, h, tableBase, tableType);\n}\n\n/* LZ4_getIndexOnHash() :\n * Index of match position registered in hash table.\n * hash position must be calculated by using base+index, or dictBase+index.\n * Assumption 1 : only valid if tableType == byU32 or byU16.\n * Assumption 2 : h is presumed valid (within limits of hash table)\n */\nLZ4_FORCE_INLINE U32 LZ4_getIndexOnHash(U32 h, const void* tableBase, tableType_t tableType)\n{\n    LZ4_STATIC_ASSERT(LZ4_MEMORY_USAGE > 2);\n    if (tableType == byU32) {\n        const U32* const hashTable = (const U32*) tableBase;\n        assert(h < (1U << (LZ4_MEMORY_USAGE-2)));\n        return hashTable[h];\n    }\n    if (tableType == byU16) {\n        const U16* const hashTable = (const U16*) tableBase;\n        assert(h < (1U << (LZ4_MEMORY_USAGE-1)));\n        return hashTable[h];\n    }\n    assert(0); return 0;  /* forbidden case */\n}\n\nstatic const BYTE* LZ4_getPositionOnHash(U32 h, const void* tableBase, tableType_t tableType)\n{\n    assert(tableType == byPtr); (void)tableType;\n    { const BYTE* const* hashTable = (const BYTE* const*) tableBase; return hashTable[h]; }\n}\n\nLZ4_FORCE_INLINE const BYTE*\nLZ4_getPosition(const BYTE* p,\n                const void* tableBase, tableType_t tableType)\n{\n    U32 const h = LZ4_hashPosition(p, tableType);\n    return LZ4_getPositionOnHash(h, tableBase, tableType);\n}\n\nLZ4_FORCE_INLINE void\nLZ4_prepareTable(LZ4_stream_t_internal* const cctx,\n           const int inputSize,\n           const tableType_t tableType) {\n    /* If the table hasn't been used, it's guaranteed to be zeroed out, and is\n     * therefore safe to use no matter what mode we're in. Otherwise, we figure\n     * out if it's safe to leave as is or whether it needs to be reset.\n     */\n    if ((tableType_t)cctx->tableType != clearedTable) {\n        assert(inputSize >= 0);\n        if ((tableType_t)cctx->tableType != tableType\n          || ((tableType == byU16) && cctx->currentOffset + (unsigned)inputSize >= 0xFFFFU)\n          || ((tableType == byU32) && cctx->currentOffset > 1 GB)\n          || tableType == byPtr\n          || inputSize >= 4 KB)\n        {\n            DEBUGLOG(4, \"LZ4_prepareTable: Resetting table in %p\", cctx);\n            MEM_INIT(cctx->hashTable, 0, LZ4_HASHTABLESIZE);\n            cctx->currentOffset = 0;\n            cctx->tableType = (U32)clearedTable;\n        } else {\n            DEBUGLOG(4, \"LZ4_prepareTable: Re-use hash table (no reset)\");\n        }\n    }\n\n    /* Adding a gap, so all previous entries are > LZ4_DISTANCE_MAX back,\n     * is faster than compressing without a gap.\n     * However, compressing with currentOffset == 0 is faster still,\n     * so we preserve that case.\n     */\n    if (cctx->currentOffset != 0 && tableType == byU32) {\n        DEBUGLOG(5, \"LZ4_prepareTable: adding 64KB to currentOffset\");\n        cctx->currentOffset += 64 KB;\n    }\n\n    /* Finally, clear history */\n    cctx->dictCtx = NULL;\n    cctx->dictionary = NULL;\n    cctx->dictSize = 0;\n}\n\n/** LZ4_compress_generic() :\n *  inlined, to ensure branches are decided at compilation time.\n *  The following conditions are presumed already validated:\n *  - source != NULL\n *  - inputSize > 0\n */\nLZ4_FORCE_INLINE int LZ4_compress_generic_validated(\n                 LZ4_stream_t_internal* const cctx,\n                 const char* const source,\n                 char* const dest,\n                 const int inputSize,\n                 int*  inputConsumed, /* only written when outputDirective == fillOutput */\n                 const int maxOutputSize,\n                 const limitedOutput_directive outputDirective,\n                 const tableType_t tableType,\n                 const dict_directive dictDirective,\n                 const dictIssue_directive dictIssue,\n                 const int acceleration)\n{\n    int result;\n    const BYTE* ip = (const BYTE*)source;\n\n    U32 const startIndex = cctx->currentOffset;\n    const BYTE* base = (const BYTE*)source - startIndex;\n    const BYTE* lowLimit;\n\n    const LZ4_stream_t_internal* dictCtx = (const LZ4_stream_t_internal*) cctx->dictCtx;\n    const BYTE* const dictionary =\n        dictDirective == usingDictCtx ? dictCtx->dictionary : cctx->dictionary;\n    const U32 dictSize =\n        dictDirective == usingDictCtx ? dictCtx->dictSize : cctx->dictSize;\n    const U32 dictDelta =\n        (dictDirective == usingDictCtx) ? startIndex - dictCtx->currentOffset : 0;   /* make indexes in dictCtx comparable with indexes in current context */\n\n    int const maybe_extMem = (dictDirective == usingExtDict) || (dictDirective == usingDictCtx);\n    U32 const prefixIdxLimit = startIndex - dictSize;   /* used when dictDirective == dictSmall */\n    const BYTE* const dictEnd = dictionary ? dictionary + dictSize : dictionary;\n    const BYTE* anchor = (const BYTE*) source;\n    const BYTE* const iend = ip + inputSize;\n    const BYTE* const mflimitPlusOne = iend - MFLIMIT + 1;\n    const BYTE* const matchlimit = iend - LASTLITERALS;\n\n    /* the dictCtx currentOffset is indexed on the start of the dictionary,\n     * while a dictionary in the current context precedes the currentOffset */\n    const BYTE* dictBase = (dictionary == NULL) ? NULL :\n                           (dictDirective == usingDictCtx) ?\n                            dictionary + dictSize - dictCtx->currentOffset :\n                            dictionary + dictSize - startIndex;\n\n    BYTE* op = (BYTE*) dest;\n    BYTE* const olimit = op + maxOutputSize;\n\n    U32 offset = 0;\n    U32 forwardH;\n\n    DEBUGLOG(5, \"LZ4_compress_generic_validated: srcSize=%i, tableType=%u\", inputSize, tableType);\n    assert(ip != NULL);\n    if (tableType == byU16) assert(inputSize<LZ4_64Klimit);  /* Size too large (not within 64K limit) */\n    if (tableType == byPtr) assert(dictDirective==noDict);   /* only supported use case with byPtr */\n    /* If init conditions are not met, we don't have to mark stream\n     * as having dirty context, since no action was taken yet */\n    if (outputDirective == fillOutput && maxOutputSize < 1) { return 0; } /* Impossible to store anything */\n    assert(acceleration >= 1);\n\n    lowLimit = (const BYTE*)source - (dictDirective == withPrefix64k ? dictSize : 0);\n\n    /* Update context state */\n    if (dictDirective == usingDictCtx) {\n        /* Subsequent linked blocks can't use the dictionary. */\n        /* Instead, they use the block we just compressed. */\n        cctx->dictCtx = NULL;\n        cctx->dictSize = (U32)inputSize;\n    } else {\n        cctx->dictSize += (U32)inputSize;\n    }\n    cctx->currentOffset += (U32)inputSize;\n    cctx->tableType = (U32)tableType;\n\n    if (inputSize<LZ4_minLength) goto _last_literals;        /* Input too small, no compression (all literals) */\n\n    /* First Byte */\n    {   U32 const h = LZ4_hashPosition(ip, tableType);\n        if (tableType == byPtr) {\n            LZ4_putPositionOnHash(ip, h, cctx->hashTable, byPtr);\n        } else {\n            LZ4_putIndexOnHash(startIndex, h, cctx->hashTable, tableType);\n    }   }\n    ip++; forwardH = LZ4_hashPosition(ip, tableType);\n\n    /* Main Loop */\n    for ( ; ; ) {\n        const BYTE* match;\n        BYTE* token;\n        const BYTE* filledIp;\n\n        /* Find a match */\n        if (tableType == byPtr) {\n            const BYTE* forwardIp = ip;\n            int step = 1;\n            int searchMatchNb = acceleration << LZ4_skipTrigger;\n            do {\n                U32 const h = forwardH;\n                ip = forwardIp;\n                forwardIp += step;\n                step = (searchMatchNb++ >> LZ4_skipTrigger);\n\n                if (unlikely(forwardIp > mflimitPlusOne)) goto _last_literals;\n                assert(ip < mflimitPlusOne);\n\n                match = LZ4_getPositionOnHash(h, cctx->hashTable, tableType);\n                forwardH = LZ4_hashPosition(forwardIp, tableType);\n                LZ4_putPositionOnHash(ip, h, cctx->hashTable, tableType);\n\n            } while ( (match+LZ4_DISTANCE_MAX < ip)\n                   || (LZ4_read32(match) != LZ4_read32(ip)) );\n\n        } else {   /* byU32, byU16 */\n\n            const BYTE* forwardIp = ip;\n            int step = 1;\n            int searchMatchNb = acceleration << LZ4_skipTrigger;\n            do {\n                U32 const h = forwardH;\n                U32 const current = (U32)(forwardIp - base);\n                U32 matchIndex = LZ4_getIndexOnHash(h, cctx->hashTable, tableType);\n                assert(matchIndex <= current);\n                assert(forwardIp - base < (ptrdiff_t)(2 GB - 1));\n                ip = forwardIp;\n                forwardIp += step;\n                step = (searchMatchNb++ >> LZ4_skipTrigger);\n\n                if (unlikely(forwardIp > mflimitPlusOne)) goto _last_literals;\n                assert(ip < mflimitPlusOne);\n\n                if (dictDirective == usingDictCtx) {\n                    if (matchIndex < startIndex) {\n                        /* there was no match, try the dictionary */\n                        assert(tableType == byU32);\n                        matchIndex = LZ4_getIndexOnHash(h, dictCtx->hashTable, byU32);\n                        match = dictBase + matchIndex;\n                        matchIndex += dictDelta;   /* make dictCtx index comparable with current context */\n                        lowLimit = dictionary;\n                    } else {\n                        match = base + matchIndex;\n                        lowLimit = (const BYTE*)source;\n                    }\n                } else if (dictDirective == usingExtDict) {\n                    if (matchIndex < startIndex) {\n                        DEBUGLOG(7, \"extDict candidate: matchIndex=%5u  <  startIndex=%5u\", matchIndex, startIndex);\n                        assert(startIndex - matchIndex >= MINMATCH);\n                        assert(dictBase);\n                        match = dictBase + matchIndex;\n                        lowLimit = dictionary;\n                    } else {\n                        match = base + matchIndex;\n                        lowLimit = (const BYTE*)source;\n                    }\n                } else {   /* single continuous memory segment */\n                    match = base + matchIndex;\n                }\n                forwardH = LZ4_hashPosition(forwardIp, tableType);\n                LZ4_putIndexOnHash(current, h, cctx->hashTable, tableType);\n\n                DEBUGLOG(7, \"candidate at pos=%u  (offset=%u \\n\", matchIndex, current - matchIndex);\n                if ((dictIssue == dictSmall) && (matchIndex < prefixIdxLimit)) { continue; }    /* match outside of valid area */\n                assert(matchIndex < current);\n                if ( ((tableType != byU16) || (LZ4_DISTANCE_MAX < LZ4_DISTANCE_ABSOLUTE_MAX))\n                  && (matchIndex+LZ4_DISTANCE_MAX < current)) {\n                    continue;\n                } /* too far */\n                assert((current - matchIndex) <= LZ4_DISTANCE_MAX);  /* match now expected within distance */\n\n                if (LZ4_read32(match) == LZ4_read32(ip)) {\n                    if (maybe_extMem) offset = current - matchIndex;\n                    break;   /* match found */\n                }\n\n            } while(1);\n        }\n\n        /* Catch up */\n        filledIp = ip;\n        while (((ip>anchor) & (match > lowLimit)) && (unlikely(ip[-1]==match[-1]))) { ip--; match--; }\n\n        /* Encode Literals */\n        {   unsigned const litLength = (unsigned)(ip - anchor);\n            token = op++;\n            if ((outputDirective == limitedOutput) &&  /* Check output buffer overflow */\n                (unlikely(op + litLength + (2 + 1 + LASTLITERALS) + (litLength/255) > olimit)) ) {\n                return 0;   /* cannot compress within `dst` budget. Stored indexes in hash table are nonetheless fine */\n            }\n            if ((outputDirective == fillOutput) &&\n                (unlikely(op + (litLength+240)/255 /* litlen */ + litLength /* literals */ + 2 /* offset */ + 1 /* token */ + MFLIMIT - MINMATCH /* min last literals so last match is <= end - MFLIMIT */ > olimit))) {\n                op--;\n                goto _last_literals;\n            }\n            if (litLength >= RUN_MASK) {\n                int len = (int)(litLength - RUN_MASK);\n                *token = (RUN_MASK<<ML_BITS);\n                for(; len >= 255 ; len-=255) *op++ = 255;\n                *op++ = (BYTE)len;\n            }\n            else *token = (BYTE)(litLength<<ML_BITS);\n\n            /* Copy Literals */\n            LZ4_wildCopy8(op, anchor, op+litLength);\n            op+=litLength;\n            DEBUGLOG(6, \"seq.start:%i, literals=%u, match.start:%i\",\n                        (int)(anchor-(const BYTE*)source), litLength, (int)(ip-(const BYTE*)source));\n        }\n\n_next_match:\n        /* at this stage, the following variables must be correctly set :\n         * - ip : at start of LZ operation\n         * - match : at start of previous pattern occurrence; can be within current prefix, or within extDict\n         * - offset : if maybe_ext_memSegment==1 (constant)\n         * - lowLimit : must be == dictionary to mean \"match is within extDict\"; must be == source otherwise\n         * - token and *token : position to write 4-bits for match length; higher 4-bits for literal length supposed already written\n         */\n\n        if ((outputDirective == fillOutput) &&\n            (op + 2 /* offset */ + 1 /* token */ + MFLIMIT - MINMATCH /* min last literals so last match is <= end - MFLIMIT */ > olimit)) {\n            /* the match was too close to the end, rewind and go to last literals */\n            op = token;\n            goto _last_literals;\n        }\n\n        /* Encode Offset */\n        if (maybe_extMem) {   /* static test */\n            DEBUGLOG(6, \"             with offset=%u  (ext if > %i)\", offset, (int)(ip - (const BYTE*)source));\n            assert(offset <= LZ4_DISTANCE_MAX && offset > 0);\n            LZ4_writeLE16(op, (U16)offset); op+=2;\n        } else  {\n            DEBUGLOG(6, \"             with offset=%u  (same segment)\", (U32)(ip - match));\n            assert(ip-match <= LZ4_DISTANCE_MAX);\n            LZ4_writeLE16(op, (U16)(ip - match)); op+=2;\n        }\n\n        /* Encode MatchLength */\n        {   unsigned matchCode;\n\n            if ( (dictDirective==usingExtDict || dictDirective==usingDictCtx)\n              && (lowLimit==dictionary) /* match within extDict */ ) {\n                const BYTE* limit = ip + (dictEnd-match);\n                assert(dictEnd > match);\n                if (limit > matchlimit) limit = matchlimit;\n                matchCode = LZ4_count(ip+MINMATCH, match+MINMATCH, limit);\n                ip += (size_t)matchCode + MINMATCH;\n                if (ip==limit) {\n                    unsigned const more = LZ4_count(limit, (const BYTE*)source, matchlimit);\n                    matchCode += more;\n                    ip += more;\n                }\n                DEBUGLOG(6, \"             with matchLength=%u starting in extDict\", matchCode+MINMATCH);\n            } else {\n                matchCode = LZ4_count(ip+MINMATCH, match+MINMATCH, matchlimit);\n                ip += (size_t)matchCode + MINMATCH;\n                DEBUGLOG(6, \"             with matchLength=%u\", matchCode+MINMATCH);\n            }\n\n            if ((outputDirective) &&    /* Check output buffer overflow */\n                (unlikely(op + (1 + LASTLITERALS) + (matchCode+240)/255 > olimit)) ) {\n                if (outputDirective == fillOutput) {\n                    /* Match description too long : reduce it */\n                    U32 newMatchCode = 15 /* in token */ - 1 /* to avoid needing a zero byte */ + ((U32)(olimit - op) - 1 - LASTLITERALS) * 255;\n                    ip -= matchCode - newMatchCode;\n                    assert(newMatchCode < matchCode);\n                    matchCode = newMatchCode;\n                    if (unlikely(ip <= filledIp)) {\n                        /* We have already filled up to filledIp so if ip ends up less than filledIp\n                         * we have positions in the hash table beyond the current position. This is\n                         * a problem if we reuse the hash table. So we have to remove these positions\n                         * from the hash table.\n                         */\n                        const BYTE* ptr;\n                        DEBUGLOG(5, \"Clearing %u positions\", (U32)(filledIp - ip));\n                        for (ptr = ip; ptr <= filledIp; ++ptr) {\n                            U32 const h = LZ4_hashPosition(ptr, tableType);\n                            LZ4_clearHash(h, cctx->hashTable, tableType);\n                        }\n                    }\n                } else {\n                    assert(outputDirective == limitedOutput);\n                    return 0;   /* cannot compress within `dst` budget. Stored indexes in hash table are nonetheless fine */\n                }\n            }\n            if (matchCode >= ML_MASK) {\n                *token += ML_MASK;\n                matchCode -= ML_MASK;\n                LZ4_write32(op, 0xFFFFFFFF);\n                while (matchCode >= 4*255) {\n                    op+=4;\n                    LZ4_write32(op, 0xFFFFFFFF);\n                    matchCode -= 4*255;\n                }\n                op += matchCode / 255;\n                *op++ = (BYTE)(matchCode % 255);\n            } else\n                *token += (BYTE)(matchCode);\n        }\n        /* Ensure we have enough space for the last literals. */\n        assert(!(outputDirective == fillOutput && op + 1 + LASTLITERALS > olimit));\n\n        anchor = ip;\n\n        /* Test end of chunk */\n        if (ip >= mflimitPlusOne) break;\n\n        /* Fill table */\n        {   U32 const h = LZ4_hashPosition(ip-2, tableType);\n            if (tableType == byPtr) {\n                LZ4_putPositionOnHash(ip-2, h, cctx->hashTable, byPtr);\n            } else {\n                U32 const idx = (U32)((ip-2) - base);\n                LZ4_putIndexOnHash(idx, h, cctx->hashTable, tableType);\n        }   }\n\n        /* Test next position */\n        if (tableType == byPtr) {\n\n            match = LZ4_getPosition(ip, cctx->hashTable, tableType);\n            LZ4_putPosition(ip, cctx->hashTable, tableType);\n            if ( (match+LZ4_DISTANCE_MAX >= ip)\n              && (LZ4_read32(match) == LZ4_read32(ip)) )\n            { token=op++; *token=0; goto _next_match; }\n\n        } else {   /* byU32, byU16 */\n\n            U32 const h = LZ4_hashPosition(ip, tableType);\n            U32 const current = (U32)(ip-base);\n            U32 matchIndex = LZ4_getIndexOnHash(h, cctx->hashTable, tableType);\n            assert(matchIndex < current);\n            if (dictDirective == usingDictCtx) {\n                if (matchIndex < startIndex) {\n                    /* there was no match, try the dictionary */\n                    assert(tableType == byU32);\n                    matchIndex = LZ4_getIndexOnHash(h, dictCtx->hashTable, byU32);\n                    match = dictBase + matchIndex;\n                    lowLimit = dictionary;   /* required for match length counter */\n                    matchIndex += dictDelta;\n                } else {\n                    match = base + matchIndex;\n                    lowLimit = (const BYTE*)source;  /* required for match length counter */\n                }\n            } else if (dictDirective==usingExtDict) {\n                if (matchIndex < startIndex) {\n                    assert(dictBase);\n                    match = dictBase + matchIndex;\n                    lowLimit = dictionary;   /* required for match length counter */\n                } else {\n                    match = base + matchIndex;\n                    lowLimit = (const BYTE*)source;   /* required for match length counter */\n                }\n            } else {   /* single memory segment */\n                match = base + matchIndex;\n            }\n            LZ4_putIndexOnHash(current, h, cctx->hashTable, tableType);\n            assert(matchIndex < current);\n            if ( ((dictIssue==dictSmall) ? (matchIndex >= prefixIdxLimit) : 1)\n              && (((tableType==byU16) && (LZ4_DISTANCE_MAX == LZ4_DISTANCE_ABSOLUTE_MAX)) ? 1 : (matchIndex+LZ4_DISTANCE_MAX >= current))\n              && (LZ4_read32(match) == LZ4_read32(ip)) ) {\n                token=op++;\n                *token=0;\n                if (maybe_extMem) offset = current - matchIndex;\n                DEBUGLOG(6, \"seq.start:%i, literals=%u, match.start:%i\",\n                            (int)(anchor-(const BYTE*)source), 0, (int)(ip-(const BYTE*)source));\n                goto _next_match;\n            }\n        }\n\n        /* Prepare next loop */\n        forwardH = LZ4_hashPosition(++ip, tableType);\n\n    }\n\n_last_literals:\n    /* Encode Last Literals */\n    {   size_t lastRun = (size_t)(iend - anchor);\n        if ( (outputDirective) &&  /* Check output buffer overflow */\n            (op + lastRun + 1 + ((lastRun+255-RUN_MASK)/255) > olimit)) {\n            if (outputDirective == fillOutput) {\n                /* adapt lastRun to fill 'dst' */\n                assert(olimit >= op);\n                lastRun  = (size_t)(olimit-op) - 1/*token*/;\n                lastRun -= (lastRun + 256 - RUN_MASK) / 256;  /*additional length tokens*/\n            } else {\n                assert(outputDirective == limitedOutput);\n                return 0;   /* cannot compress within `dst` budget. Stored indexes in hash table are nonetheless fine */\n            }\n        }\n        DEBUGLOG(6, \"Final literal run : %i literals\", (int)lastRun);\n        if (lastRun >= RUN_MASK) {\n            size_t accumulator = lastRun - RUN_MASK;\n            *op++ = RUN_MASK << ML_BITS;\n            for(; accumulator >= 255 ; accumulator-=255) *op++ = 255;\n            *op++ = (BYTE) accumulator;\n        } else {\n            *op++ = (BYTE)(lastRun<<ML_BITS);\n        }\n        LZ4_memcpy(op, anchor, lastRun);\n        ip = anchor + lastRun;\n        op += lastRun;\n    }\n\n    if (outputDirective == fillOutput) {\n        *inputConsumed = (int) (((const char*)ip)-source);\n    }\n    result = (int)(((char*)op) - dest);\n    assert(result > 0);\n    DEBUGLOG(5, \"LZ4_compress_generic: compressed %i bytes into %i bytes\", inputSize, result);\n    return result;\n}\n\n/** LZ4_compress_generic() :\n *  inlined, to ensure branches are decided at compilation time;\n *  takes care of src == (NULL, 0)\n *  and forward the rest to LZ4_compress_generic_validated */\nLZ4_FORCE_INLINE int LZ4_compress_generic(\n                 LZ4_stream_t_internal* const cctx,\n                 const char* const src,\n                 char* const dst,\n                 const int srcSize,\n                 int *inputConsumed, /* only written when outputDirective == fillOutput */\n                 const int dstCapacity,\n                 const limitedOutput_directive outputDirective,\n                 const tableType_t tableType,\n                 const dict_directive dictDirective,\n                 const dictIssue_directive dictIssue,\n                 const int acceleration)\n{\n    DEBUGLOG(5, \"LZ4_compress_generic: srcSize=%i, dstCapacity=%i\",\n                srcSize, dstCapacity);\n\n    if ((U32)srcSize > (U32)LZ4_MAX_INPUT_SIZE) { return 0; }  /* Unsupported srcSize, too large (or negative) */\n    if (srcSize == 0) {   /* src == NULL supported if srcSize == 0 */\n        if (outputDirective != notLimited && dstCapacity <= 0) return 0;  /* no output, can't write anything */\n        DEBUGLOG(5, \"Generating an empty block\");\n        assert(outputDirective == notLimited || dstCapacity >= 1);\n        assert(dst != NULL);\n        dst[0] = 0;\n        if (outputDirective == fillOutput) {\n            assert (inputConsumed != NULL);\n            *inputConsumed = 0;\n        }\n        return 1;\n    }\n    assert(src != NULL);\n\n    return LZ4_compress_generic_validated(cctx, src, dst, srcSize,\n                inputConsumed, /* only written into if outputDirective == fillOutput */\n                dstCapacity, outputDirective,\n                tableType, dictDirective, dictIssue, acceleration);\n}\n\n\nint LZ4_compress_fast_extState(void* state, const char* source, char* dest, int inputSize, int maxOutputSize, int acceleration)\n{\n    LZ4_stream_t_internal* const ctx = & LZ4_initStream(state, sizeof(LZ4_stream_t)) -> internal_donotuse;\n    assert(ctx != NULL);\n    if (acceleration < 1) acceleration = LZ4_ACCELERATION_DEFAULT;\n    if (acceleration > LZ4_ACCELERATION_MAX) acceleration = LZ4_ACCELERATION_MAX;\n    if (maxOutputSize >= LZ4_compressBound(inputSize)) {\n        if (inputSize < LZ4_64Klimit) {\n            return LZ4_compress_generic(ctx, source, dest, inputSize, NULL, 0, notLimited, byU16, noDict, noDictIssue, acceleration);\n        } else {\n            const tableType_t tableType = ((sizeof(void*)==4) && ((uptrval)source > LZ4_DISTANCE_MAX)) ? byPtr : byU32;\n            return LZ4_compress_generic(ctx, source, dest, inputSize, NULL, 0, notLimited, tableType, noDict, noDictIssue, acceleration);\n        }\n    } else {\n        if (inputSize < LZ4_64Klimit) {\n            return LZ4_compress_generic(ctx, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, byU16, noDict, noDictIssue, acceleration);\n        } else {\n            const tableType_t tableType = ((sizeof(void*)==4) && ((uptrval)source > LZ4_DISTANCE_MAX)) ? byPtr : byU32;\n            return LZ4_compress_generic(ctx, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, noDict, noDictIssue, acceleration);\n        }\n    }\n}\n\n/**\n * LZ4_compress_fast_extState_fastReset() :\n * A variant of LZ4_compress_fast_extState().\n *\n * Using this variant avoids an expensive initialization step. It is only safe\n * to call if the state buffer is known to be correctly initialized already\n * (see comment in lz4.h on LZ4_resetStream_fast() for a definition of\n * \"correctly initialized\").\n */\nint LZ4_compress_fast_extState_fastReset(void* state, const char* src, char* dst, int srcSize, int dstCapacity, int acceleration)\n{\n    LZ4_stream_t_internal* const ctx = &((LZ4_stream_t*)state)->internal_donotuse;\n    if (acceleration < 1) acceleration = LZ4_ACCELERATION_DEFAULT;\n    if (acceleration > LZ4_ACCELERATION_MAX) acceleration = LZ4_ACCELERATION_MAX;\n    assert(ctx != NULL);\n\n    if (dstCapacity >= LZ4_compressBound(srcSize)) {\n        if (srcSize < LZ4_64Klimit) {\n            const tableType_t tableType = byU16;\n            LZ4_prepareTable(ctx, srcSize, tableType);\n            if (ctx->currentOffset) {\n                return LZ4_compress_generic(ctx, src, dst, srcSize, NULL, 0, notLimited, tableType, noDict, dictSmall, acceleration);\n            } else {\n                return LZ4_compress_generic(ctx, src, dst, srcSize, NULL, 0, notLimited, tableType, noDict, noDictIssue, acceleration);\n            }\n        } else {\n            const tableType_t tableType = ((sizeof(void*)==4) && ((uptrval)src > LZ4_DISTANCE_MAX)) ? byPtr : byU32;\n            LZ4_prepareTable(ctx, srcSize, tableType);\n            return LZ4_compress_generic(ctx, src, dst, srcSize, NULL, 0, notLimited, tableType, noDict, noDictIssue, acceleration);\n        }\n    } else {\n        if (srcSize < LZ4_64Klimit) {\n            const tableType_t tableType = byU16;\n            LZ4_prepareTable(ctx, srcSize, tableType);\n            if (ctx->currentOffset) {\n                return LZ4_compress_generic(ctx, src, dst, srcSize, NULL, dstCapacity, limitedOutput, tableType, noDict, dictSmall, acceleration);\n            } else {\n                return LZ4_compress_generic(ctx, src, dst, srcSize, NULL, dstCapacity, limitedOutput, tableType, noDict, noDictIssue, acceleration);\n            }\n        } else {\n            const tableType_t tableType = ((sizeof(void*)==4) && ((uptrval)src > LZ4_DISTANCE_MAX)) ? byPtr : byU32;\n            LZ4_prepareTable(ctx, srcSize, tableType);\n            return LZ4_compress_generic(ctx, src, dst, srcSize, NULL, dstCapacity, limitedOutput, tableType, noDict, noDictIssue, acceleration);\n        }\n    }\n}\n\n\nint LZ4_compress_fast(const char* src, char* dest, int srcSize, int dstCapacity, int acceleration)\n{\n    int result;\n#if (LZ4_HEAPMODE)\n    LZ4_stream_t* const ctxPtr = (LZ4_stream_t*)ALLOC(sizeof(LZ4_stream_t));   /* malloc-calloc always properly aligned */\n    if (ctxPtr == NULL) return 0;\n#else\n    LZ4_stream_t ctx;\n    LZ4_stream_t* const ctxPtr = &ctx;\n#endif\n    result = LZ4_compress_fast_extState(ctxPtr, src, dest, srcSize, dstCapacity, acceleration);\n\n#if (LZ4_HEAPMODE)\n    FREEMEM(ctxPtr);\n#endif\n    return result;\n}\n\n\nint LZ4_compress_default(const char* src, char* dst, int srcSize, int dstCapacity)\n{\n    return LZ4_compress_fast(src, dst, srcSize, dstCapacity, 1);\n}\n\n\n/* Note!: This function leaves the stream in an unclean/broken state!\n * It is not safe to subsequently use the same state with a _fastReset() or\n * _continue() call without resetting it. */\nstatic int LZ4_compress_destSize_extState (LZ4_stream_t* state, const char* src, char* dst, int* srcSizePtr, int targetDstSize)\n{\n    void* const s = LZ4_initStream(state, sizeof (*state));\n    assert(s != NULL); (void)s;\n\n    if (targetDstSize >= LZ4_compressBound(*srcSizePtr)) {  /* compression success is guaranteed */\n        return LZ4_compress_fast_extState(state, src, dst, *srcSizePtr, targetDstSize, 1);\n    } else {\n        if (*srcSizePtr < LZ4_64Klimit) {\n            return LZ4_compress_generic(&state->internal_donotuse, src, dst, *srcSizePtr, srcSizePtr, targetDstSize, fillOutput, byU16, noDict, noDictIssue, 1);\n        } else {\n            tableType_t const addrMode = ((sizeof(void*)==4) && ((uptrval)src > LZ4_DISTANCE_MAX)) ? byPtr : byU32;\n            return LZ4_compress_generic(&state->internal_donotuse, src, dst, *srcSizePtr, srcSizePtr, targetDstSize, fillOutput, addrMode, noDict, noDictIssue, 1);\n    }   }\n}\n\n\nint LZ4_compress_destSize(const char* src, char* dst, int* srcSizePtr, int targetDstSize)\n{\n#if (LZ4_HEAPMODE)\n    LZ4_stream_t* const ctx = (LZ4_stream_t*)ALLOC(sizeof(LZ4_stream_t));   /* malloc-calloc always properly aligned */\n    if (ctx == NULL) return 0;\n#else\n    LZ4_stream_t ctxBody;\n    LZ4_stream_t* const ctx = &ctxBody;\n#endif\n\n    int result = LZ4_compress_destSize_extState(ctx, src, dst, srcSizePtr, targetDstSize);\n\n#if (LZ4_HEAPMODE)\n    FREEMEM(ctx);\n#endif\n    return result;\n}\n\n\n\n/*-******************************\n*  Streaming functions\n********************************/\n\n#if !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION)\nLZ4_stream_t* LZ4_createStream(void)\n{\n    LZ4_stream_t* const lz4s = (LZ4_stream_t*)ALLOC(sizeof(LZ4_stream_t));\n    LZ4_STATIC_ASSERT(sizeof(LZ4_stream_t) >= sizeof(LZ4_stream_t_internal));\n    DEBUGLOG(4, \"LZ4_createStream %p\", lz4s);\n    if (lz4s == NULL) return NULL;\n    LZ4_initStream(lz4s, sizeof(*lz4s));\n    return lz4s;\n}\n#endif\n\nstatic size_t LZ4_stream_t_alignment(void)\n{\n#if LZ4_ALIGN_TEST\n    typedef struct { char c; LZ4_stream_t t; } t_a;\n    return sizeof(t_a) - sizeof(LZ4_stream_t);\n#else\n    return 1;  /* effectively disabled */\n#endif\n}\n\nLZ4_stream_t* LZ4_initStream (void* buffer, size_t size)\n{\n    DEBUGLOG(5, \"LZ4_initStream\");\n    if (buffer == NULL) { return NULL; }\n    if (size < sizeof(LZ4_stream_t)) { return NULL; }\n    if (!LZ4_isAligned(buffer, LZ4_stream_t_alignment())) return NULL;\n    MEM_INIT(buffer, 0, sizeof(LZ4_stream_t_internal));\n    return (LZ4_stream_t*)buffer;\n}\n\n/* resetStream is now deprecated,\n * prefer initStream() which is more general */\nvoid LZ4_resetStream (LZ4_stream_t* LZ4_stream)\n{\n    DEBUGLOG(5, \"LZ4_resetStream (ctx:%p)\", LZ4_stream);\n    MEM_INIT(LZ4_stream, 0, sizeof(LZ4_stream_t_internal));\n}\n\nvoid LZ4_resetStream_fast(LZ4_stream_t* ctx) {\n    LZ4_prepareTable(&(ctx->internal_donotuse), 0, byU32);\n}\n\n#if !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION)\nint LZ4_freeStream (LZ4_stream_t* LZ4_stream)\n{\n    if (!LZ4_stream) return 0;   /* support free on NULL */\n    DEBUGLOG(5, \"LZ4_freeStream %p\", LZ4_stream);\n    FREEMEM(LZ4_stream);\n    return (0);\n}\n#endif\n\n\n#define HASH_UNIT sizeof(reg_t)\nint LZ4_loadDict (LZ4_stream_t* LZ4_dict, const char* dictionary, int dictSize)\n{\n    LZ4_stream_t_internal* const dict = &LZ4_dict->internal_donotuse;\n    const tableType_t tableType = byU32;\n    const BYTE* p = (const BYTE*)dictionary;\n    const BYTE* const dictEnd = p + dictSize;\n    U32 idx32;\n\n    DEBUGLOG(4, \"LZ4_loadDict (%i bytes from %p into %p)\", dictSize, dictionary, LZ4_dict);\n\n    /* It's necessary to reset the context,\n     * and not just continue it with prepareTable()\n     * to avoid any risk of generating overflowing matchIndex\n     * when compressing using this dictionary */\n    LZ4_resetStream(LZ4_dict);\n\n    /* We always increment the offset by 64 KB, since, if the dict is longer,\n     * we truncate it to the last 64k, and if it's shorter, we still want to\n     * advance by a whole window length so we can provide the guarantee that\n     * there are only valid offsets in the window, which allows an optimization\n     * in LZ4_compress_fast_continue() where it uses noDictIssue even when the\n     * dictionary isn't a full 64k. */\n    dict->currentOffset += 64 KB;\n\n    if (dictSize < (int)HASH_UNIT) {\n        return 0;\n    }\n\n    if ((dictEnd - p) > 64 KB) p = dictEnd - 64 KB;\n    dict->dictionary = p;\n    dict->dictSize = (U32)(dictEnd - p);\n    dict->tableType = (U32)tableType;\n    idx32 = dict->currentOffset - dict->dictSize;\n\n    while (p <= dictEnd-HASH_UNIT) {\n        U32 const h = LZ4_hashPosition(p, tableType);\n        LZ4_putIndexOnHash(idx32, h, dict->hashTable, tableType);\n        p+=3; idx32+=3;\n    }\n\n    return (int)dict->dictSize;\n}\n\nvoid LZ4_attach_dictionary(LZ4_stream_t* workingStream, const LZ4_stream_t* dictionaryStream)\n{\n    const LZ4_stream_t_internal* dictCtx = (dictionaryStream == NULL) ? NULL :\n        &(dictionaryStream->internal_donotuse);\n\n    DEBUGLOG(4, \"LZ4_attach_dictionary (%p, %p, size %u)\",\n             workingStream, dictionaryStream,\n             dictCtx != NULL ? dictCtx->dictSize : 0);\n\n    if (dictCtx != NULL) {\n        /* If the current offset is zero, we will never look in the\n         * external dictionary context, since there is no value a table\n         * entry can take that indicate a miss. In that case, we need\n         * to bump the offset to something non-zero.\n         */\n        if (workingStream->internal_donotuse.currentOffset == 0) {\n            workingStream->internal_donotuse.currentOffset = 64 KB;\n        }\n\n        /* Don't actually attach an empty dictionary.\n         */\n        if (dictCtx->dictSize == 0) {\n            dictCtx = NULL;\n        }\n    }\n    workingStream->internal_donotuse.dictCtx = dictCtx;\n}\n\n\nstatic void LZ4_renormDictT(LZ4_stream_t_internal* LZ4_dict, int nextSize)\n{\n    assert(nextSize >= 0);\n    if (LZ4_dict->currentOffset + (unsigned)nextSize > 0x80000000) {   /* potential ptrdiff_t overflow (32-bits mode) */\n        /* rescale hash table */\n        U32 const delta = LZ4_dict->currentOffset - 64 KB;\n        const BYTE* dictEnd = LZ4_dict->dictionary + LZ4_dict->dictSize;\n        int i;\n        DEBUGLOG(4, \"LZ4_renormDictT\");\n        for (i=0; i<LZ4_HASH_SIZE_U32; i++) {\n            if (LZ4_dict->hashTable[i] < delta) LZ4_dict->hashTable[i]=0;\n            else LZ4_dict->hashTable[i] -= delta;\n        }\n        LZ4_dict->currentOffset = 64 KB;\n        if (LZ4_dict->dictSize > 64 KB) LZ4_dict->dictSize = 64 KB;\n        LZ4_dict->dictionary = dictEnd - LZ4_dict->dictSize;\n    }\n}\n\n\nint LZ4_compress_fast_continue (LZ4_stream_t* LZ4_stream,\n                                const char* source, char* dest,\n                                int inputSize, int maxOutputSize,\n                                int acceleration)\n{\n    const tableType_t tableType = byU32;\n    LZ4_stream_t_internal* const streamPtr = &LZ4_stream->internal_donotuse;\n    const char* dictEnd = streamPtr->dictSize ? (const char*)streamPtr->dictionary + streamPtr->dictSize : NULL;\n\n    DEBUGLOG(5, \"LZ4_compress_fast_continue (inputSize=%i, dictSize=%u)\", inputSize, streamPtr->dictSize);\n\n    LZ4_renormDictT(streamPtr, inputSize);   /* fix index overflow */\n    if (acceleration < 1) acceleration = LZ4_ACCELERATION_DEFAULT;\n    if (acceleration > LZ4_ACCELERATION_MAX) acceleration = LZ4_ACCELERATION_MAX;\n\n    /* invalidate tiny dictionaries */\n    if ( (streamPtr->dictSize < 4)     /* tiny dictionary : not enough for a hash */\n      && (dictEnd != source)           /* prefix mode */\n      && (inputSize > 0)               /* tolerance : don't lose history, in case next invocation would use prefix mode */\n      && (streamPtr->dictCtx == NULL)  /* usingDictCtx */\n      ) {\n        DEBUGLOG(5, \"LZ4_compress_fast_continue: dictSize(%u) at addr:%p is too small\", streamPtr->dictSize, streamPtr->dictionary);\n        /* remove dictionary existence from history, to employ faster prefix mode */\n        streamPtr->dictSize = 0;\n        streamPtr->dictionary = (const BYTE*)source;\n        dictEnd = source;\n    }\n\n    /* Check overlapping input/dictionary space */\n    {   const char* const sourceEnd = source + inputSize;\n        if ((sourceEnd > (const char*)streamPtr->dictionary) && (sourceEnd < dictEnd)) {\n            streamPtr->dictSize = (U32)(dictEnd - sourceEnd);\n            if (streamPtr->dictSize > 64 KB) streamPtr->dictSize = 64 KB;\n            if (streamPtr->dictSize < 4) streamPtr->dictSize = 0;\n            streamPtr->dictionary = (const BYTE*)dictEnd - streamPtr->dictSize;\n        }\n    }\n\n    /* prefix mode : source data follows dictionary */\n    if (dictEnd == source) {\n        if ((streamPtr->dictSize < 64 KB) && (streamPtr->dictSize < streamPtr->currentOffset))\n            return LZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, withPrefix64k, dictSmall, acceleration);\n        else\n            return LZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, withPrefix64k, noDictIssue, acceleration);\n    }\n\n    /* external dictionary mode */\n    {   int result;\n        if (streamPtr->dictCtx) {\n            /* We depend here on the fact that dictCtx'es (produced by\n             * LZ4_loadDict) guarantee that their tables contain no references\n             * to offsets between dictCtx->currentOffset - 64 KB and\n             * dictCtx->currentOffset - dictCtx->dictSize. This makes it safe\n             * to use noDictIssue even when the dict isn't a full 64 KB.\n             */\n            if (inputSize > 4 KB) {\n                /* For compressing large blobs, it is faster to pay the setup\n                 * cost to copy the dictionary's tables into the active context,\n                 * so that the compression loop is only looking into one table.\n                 */\n                LZ4_memcpy(streamPtr, streamPtr->dictCtx, sizeof(*streamPtr));\n                result = LZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, usingExtDict, noDictIssue, acceleration);\n            } else {\n                result = LZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, usingDictCtx, noDictIssue, acceleration);\n            }\n        } else {  /* small data <= 4 KB */\n            if ((streamPtr->dictSize < 64 KB) && (streamPtr->dictSize < streamPtr->currentOffset)) {\n                result = LZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, usingExtDict, dictSmall, acceleration);\n            } else {\n                result = LZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, usingExtDict, noDictIssue, acceleration);\n            }\n        }\n        streamPtr->dictionary = (const BYTE*)source;\n        streamPtr->dictSize = (U32)inputSize;\n        return result;\n    }\n}\n\n\n/* Hidden debug function, to force-test external dictionary mode */\nint LZ4_compress_forceExtDict (LZ4_stream_t* LZ4_dict, const char* source, char* dest, int srcSize)\n{\n    LZ4_stream_t_internal* const streamPtr = &LZ4_dict->internal_donotuse;\n    int result;\n\n    LZ4_renormDictT(streamPtr, srcSize);\n\n    if ((streamPtr->dictSize < 64 KB) && (streamPtr->dictSize < streamPtr->currentOffset)) {\n        result = LZ4_compress_generic(streamPtr, source, dest, srcSize, NULL, 0, notLimited, byU32, usingExtDict, dictSmall, 1);\n    } else {\n        result = LZ4_compress_generic(streamPtr, source, dest, srcSize, NULL, 0, notLimited, byU32, usingExtDict, noDictIssue, 1);\n    }\n\n    streamPtr->dictionary = (const BYTE*)source;\n    streamPtr->dictSize = (U32)srcSize;\n\n    return result;\n}\n\n\n/*! LZ4_saveDict() :\n *  If previously compressed data block is not guaranteed to remain available at its memory location,\n *  save it into a safer place (char* safeBuffer).\n *  Note : no need to call LZ4_loadDict() afterwards, dictionary is immediately usable,\n *         one can therefore call LZ4_compress_fast_continue() right after.\n * @return : saved dictionary size in bytes (necessarily <= dictSize), or 0 if error.\n */\nint LZ4_saveDict (LZ4_stream_t* LZ4_dict, char* safeBuffer, int dictSize)\n{\n    LZ4_stream_t_internal* const dict = &LZ4_dict->internal_donotuse;\n\n    DEBUGLOG(5, \"LZ4_saveDict : dictSize=%i, safeBuffer=%p\", dictSize, safeBuffer);\n\n    if ((U32)dictSize > 64 KB) { dictSize = 64 KB; } /* useless to define a dictionary > 64 KB */\n    if ((U32)dictSize > dict->dictSize) { dictSize = (int)dict->dictSize; }\n\n    if (safeBuffer == NULL) assert(dictSize == 0);\n    if (dictSize > 0) {\n        const BYTE* const previousDictEnd = dict->dictionary + dict->dictSize;\n        assert(dict->dictionary);\n        LZ4_memmove(safeBuffer, previousDictEnd - dictSize, (size_t)dictSize);\n    }\n\n    dict->dictionary = (const BYTE*)safeBuffer;\n    dict->dictSize = (U32)dictSize;\n\n    return dictSize;\n}\n\n\n\n/*-*******************************\n *  Decompression functions\n ********************************/\n\ntypedef enum { decode_full_block = 0, partial_decode = 1 } earlyEnd_directive;\n\n#undef MIN\n#define MIN(a,b)    ( (a) < (b) ? (a) : (b) )\n\n\n/* variant for decompress_unsafe()\n * does not know end of input\n * presumes input is well formed\n * note : will consume at least one byte */\nsize_t read_long_length_no_check(const BYTE** pp)\n{\n    size_t b, l = 0;\n    do { b = **pp; (*pp)++; l += b; } while (b==255);\n    DEBUGLOG(6, \"read_long_length_no_check: +length=%zu using %zu input bytes\", l, l/255 + 1)\n    return l;\n}\n\n/* core decoder variant for LZ4_decompress_fast*()\n * for legacy support only : these entry points are deprecated.\n * - Presumes input is correctly formed (no defense vs malformed inputs)\n * - Does not know input size (presume input buffer is \"large enough\")\n * - Decompress a full block (only)\n * @return : nb of bytes read from input.\n * Note : this variant is not optimized for speed, just for maintenance.\n *        the goal is to remove support of decompress_fast*() variants by v2.0\n**/\nLZ4_FORCE_INLINE int\nLZ4_decompress_unsafe_generic(\n                 const BYTE* const istart,\n                 BYTE* const ostart,\n                 int decompressedSize,\n\n                 size_t prefixSize,\n                 const BYTE* const dictStart,  /* only if dict==usingExtDict */\n                 const size_t dictSize         /* note: =0 if dictStart==NULL */\n                 )\n{\n    const BYTE* ip = istart;\n    BYTE* op = (BYTE*)ostart;\n    BYTE* const oend = ostart + decompressedSize;\n    const BYTE* const prefixStart = ostart - prefixSize;\n\n    DEBUGLOG(5, \"LZ4_decompress_unsafe_generic\");\n    if (dictStart == NULL) assert(dictSize == 0);\n\n    while (1) {\n        /* start new sequence */\n        unsigned token = *ip++;\n\n        /* literals */\n        {   size_t ll = token >> ML_BITS;\n            if (ll==15) {\n                /* long literal length */\n                ll += read_long_length_no_check(&ip);\n            }\n            if ((size_t)(oend-op) < ll) return -1; /* output buffer overflow */\n            LZ4_memmove(op, ip, ll); /* support in-place decompression */\n            op += ll;\n            ip += ll;\n            if ((size_t)(oend-op) < MFLIMIT) {\n                if (op==oend) break;  /* end of block */\n                DEBUGLOG(5, \"invalid: literals end at distance %zi from end of block\", oend-op);\n                /* incorrect end of block :\n                 * last match must start at least MFLIMIT==12 bytes before end of output block */\n                return -1;\n        }   }\n\n        /* match */\n        {   size_t ml = token & 15;\n            size_t const offset = LZ4_readLE16(ip);\n            ip+=2;\n\n            if (ml==15) {\n                /* long literal length */\n                ml += read_long_length_no_check(&ip);\n            }\n            ml += MINMATCH;\n\n            if ((size_t)(oend-op) < ml) return -1; /* output buffer overflow */\n\n            {   const BYTE* match = op - offset;\n\n                /* out of range */\n                if (offset > (size_t)(op - prefixStart) + dictSize) {\n                    DEBUGLOG(6, \"offset out of range\");\n                    return -1;\n                }\n\n                /* check special case : extDict */\n                if (offset > (size_t)(op - prefixStart)) {\n                    /* extDict scenario */\n                    const BYTE* const dictEnd = dictStart + dictSize;\n                    const BYTE* extMatch = dictEnd - (offset - (size_t)(op-prefixStart));\n                    size_t const extml = (size_t)(dictEnd - extMatch);\n                    if (extml > ml) {\n                        /* match entirely within extDict */\n                        LZ4_memmove(op, extMatch, ml);\n                        op += ml;\n                        ml = 0;\n                    } else {\n                        /* match split between extDict & prefix */\n                        LZ4_memmove(op, extMatch, extml);\n                        op += extml;\n                        ml -= extml;\n                    }\n                    match = prefixStart;\n                }\n\n                /* match copy - slow variant, supporting overlap copy */\n                {   size_t u;\n                    for (u=0; u<ml; u++) {\n                        op[u] = match[u];\n            }   }   }\n            op += ml;\n            if ((size_t)(oend-op) < LASTLITERALS) {\n                DEBUGLOG(5, \"invalid: match ends at distance %zi from end of block\", oend-op);\n                /* incorrect end of block :\n                 * last match must stop at least LASTLITERALS==5 bytes before end of output block */\n                return -1;\n            }\n        } /* match */\n    } /* main loop */\n    return (int)(ip - istart);\n}\n\n\n/* Read the variable-length literal or match length.\n *\n * @ip : input pointer\n * @ilimit : position after which if length is not decoded, the input is necessarily corrupted.\n * @initial_check - check ip >= ipmax before start of loop.  Returns initial_error if so.\n * @error (output) - error code.  Must be set to 0 before call.\n**/\ntypedef size_t Rvl_t;\nstatic const Rvl_t rvl_error = (Rvl_t)(-1);\nLZ4_FORCE_INLINE Rvl_t\nread_variable_length(const BYTE** ip, const BYTE* ilimit,\n                     int initial_check)\n{\n    Rvl_t s, length = 0;\n    assert(ip != NULL);\n    assert(*ip !=  NULL);\n    assert(ilimit != NULL);\n    if (initial_check && unlikely((*ip) >= ilimit)) {    /* read limit reached */\n        return rvl_error;\n    }\n    do {\n        s = **ip;\n        (*ip)++;\n        length += s;\n        if (unlikely((*ip) > ilimit)) {    /* read limit reached */\n            return rvl_error;\n        }\n        /* accumulator overflow detection (32-bit mode only) */\n        if ((sizeof(length)<8) && unlikely(length > ((Rvl_t)(-1)/2)) ) {\n            return rvl_error;\n        }\n    } while (s==255);\n\n    return length;\n}\n\n/*! LZ4_decompress_generic() :\n *  This generic decompression function covers all use cases.\n *  It shall be instantiated several times, using different sets of directives.\n *  Note that it is important for performance that this function really get inlined,\n *  in order to remove useless branches during compilation optimization.\n */\nLZ4_FORCE_INLINE int\nLZ4_decompress_generic(\n                 const char* const src,\n                 char* const dst,\n                 int srcSize,\n                 int outputSize,         /* If endOnInput==endOnInputSize, this value is `dstCapacity` */\n\n                 earlyEnd_directive partialDecoding,  /* full, partial */\n                 dict_directive dict,                 /* noDict, withPrefix64k, usingExtDict */\n                 const BYTE* const lowPrefix,  /* always <= dst, == dst when no prefix */\n                 const BYTE* const dictStart,  /* only if dict==usingExtDict */\n                 const size_t dictSize         /* note : = 0 if noDict */\n                 )\n{\n    if ((src == NULL) || (outputSize < 0)) { return -1; }\n\n    {   const BYTE* ip = (const BYTE*) src;\n        const BYTE* const iend = ip + srcSize;\n\n        BYTE* op = (BYTE*) dst;\n        BYTE* const oend = op + outputSize;\n        BYTE* cpy;\n\n        const BYTE* const dictEnd = (dictStart == NULL) ? NULL : dictStart + dictSize;\n\n        const int checkOffset = (dictSize < (int)(64 KB));\n\n\n        /* Set up the \"end\" pointers for the shortcut. */\n        const BYTE* const shortiend = iend - 14 /*maxLL*/ - 2 /*offset*/;\n        const BYTE* const shortoend = oend - 14 /*maxLL*/ - 18 /*maxML*/;\n\n        const BYTE* match;\n        size_t offset;\n        unsigned token;\n        size_t length;\n\n\n        DEBUGLOG(5, \"LZ4_decompress_generic (srcSize:%i, dstSize:%i)\", srcSize, outputSize);\n\n        /* Special cases */\n        assert(lowPrefix <= op);\n        if (unlikely(outputSize==0)) {\n            /* Empty output buffer */\n            if (partialDecoding) return 0;\n            return ((srcSize==1) && (*ip==0)) ? 0 : -1;\n        }\n        if (unlikely(srcSize==0)) { return -1; }\n\n    /* LZ4_FAST_DEC_LOOP:\n     * designed for modern OoO performance cpus,\n     * where copying reliably 32-bytes is preferable to an unpredictable branch.\n     * note : fast loop may show a regression for some client arm chips. */\n#if LZ4_FAST_DEC_LOOP\n        if ((oend - op) < FASTLOOP_SAFE_DISTANCE) {\n            DEBUGLOG(6, \"skip fast decode loop\");\n            goto safe_decode;\n        }\n\n        /* Fast loop : decode sequences as long as output < oend-FASTLOOP_SAFE_DISTANCE */\n        DEBUGLOG(6, \"using fast decode loop\");\n        while (1) {\n            /* Main fastloop assertion: We can always wildcopy FASTLOOP_SAFE_DISTANCE */\n            assert(oend - op >= FASTLOOP_SAFE_DISTANCE);\n            assert(ip < iend);\n            token = *ip++;\n            length = token >> ML_BITS;  /* literal length */\n\n            /* decode literal length */\n            if (length == RUN_MASK) {\n                size_t const addl = read_variable_length(&ip, iend-RUN_MASK, 1);\n                if (addl == rvl_error) {\n                    DEBUGLOG(6, \"error reading long literal length\");\n                    goto _output_error;\n                }\n                length += addl;\n                if (unlikely((uptrval)(op)+length<(uptrval)(op))) { goto _output_error; } /* overflow detection */\n                if (unlikely((uptrval)(ip)+length<(uptrval)(ip))) { goto _output_error; } /* overflow detection */\n\n                /* copy literals */\n                cpy = op+length;\n                LZ4_STATIC_ASSERT(MFLIMIT >= WILDCOPYLENGTH);\n                if ((cpy>oend-32) || (ip+length>iend-32)) { goto safe_literal_copy; }\n                LZ4_wildCopy32(op, ip, cpy);\n                ip += length; op = cpy;\n            } else {\n                cpy = op+length;\n                DEBUGLOG(7, \"copy %u bytes in a 16-bytes stripe\", (unsigned)length);\n                /* We don't need to check oend, since we check it once for each loop below */\n                if (ip > iend-(16 + 1/*max lit + offset + nextToken*/)) { goto safe_literal_copy; }\n                /* Literals can only be <= 14, but hope compilers optimize better when copy by a register size */\n                LZ4_memcpy(op, ip, 16);\n                ip += length; op = cpy;\n            }\n\n            /* get offset */\n            offset = LZ4_readLE16(ip); ip+=2;\n            DEBUGLOG(6, \" offset = %zu\", offset);\n            match = op - offset;\n            assert(match <= op);  /* overflow check */\n\n            /* get matchlength */\n            length = token & ML_MASK;\n\n            if (length == ML_MASK) {\n                size_t const addl = read_variable_length(&ip, iend - LASTLITERALS + 1, 0);\n                if (addl == rvl_error) {\n                    DEBUGLOG(6, \"error reading long match length\");\n                    goto _output_error;\n                }\n                length += addl;\n                length += MINMATCH;\n                if (unlikely((uptrval)(op)+length<(uptrval)op)) { goto _output_error; } /* overflow detection */\n                if ((checkOffset) && (unlikely(match + dictSize < lowPrefix))) {\n                    DEBUGLOG(6, \"Error : offset outside buffers\");\n                    goto _output_error;\n                }\n                if (op + length >= oend - FASTLOOP_SAFE_DISTANCE) {\n                    goto safe_match_copy;\n                }\n            } else {\n                length += MINMATCH;\n                if (op + length >= oend - FASTLOOP_SAFE_DISTANCE) {\n                    goto safe_match_copy;\n                }\n\n                /* Fastpath check: skip LZ4_wildCopy32 when true */\n                if ((dict == withPrefix64k) || (match >= lowPrefix)) {\n                    if (offset >= 8) {\n                        assert(match >= lowPrefix);\n                        assert(match <= op);\n                        assert(op + 18 <= oend);\n\n                        LZ4_memcpy(op, match, 8);\n                        LZ4_memcpy(op+8, match+8, 8);\n                        LZ4_memcpy(op+16, match+16, 2);\n                        op += length;\n                        continue;\n            }   }   }\n\n            if ( checkOffset && (unlikely(match + dictSize < lowPrefix)) ) {\n                DEBUGLOG(6, \"Error : pos=%zi, offset=%zi => outside buffers\", op-lowPrefix, op-match);\n                goto _output_error;\n            }\n            /* match starting within external dictionary */\n            if ((dict==usingExtDict) && (match < lowPrefix)) {\n                assert(dictEnd != NULL);\n                if (unlikely(op+length > oend-LASTLITERALS)) {\n                    if (partialDecoding) {\n                        DEBUGLOG(7, \"partialDecoding: dictionary match, close to dstEnd\");\n                        length = MIN(length, (size_t)(oend-op));\n                    } else {\n                        DEBUGLOG(6, \"end-of-block condition violated\")\n                        goto _output_error;\n                }   }\n\n                if (length <= (size_t)(lowPrefix-match)) {\n                    /* match fits entirely within external dictionary : just copy */\n                    LZ4_memmove(op, dictEnd - (lowPrefix-match), length);\n                    op += length;\n                } else {\n                    /* match stretches into both external dictionary and current block */\n                    size_t const copySize = (size_t)(lowPrefix - match);\n                    size_t const restSize = length - copySize;\n                    LZ4_memcpy(op, dictEnd - copySize, copySize);\n                    op += copySize;\n                    if (restSize > (size_t)(op - lowPrefix)) {  /* overlap copy */\n                        BYTE* const endOfMatch = op + restSize;\n                        const BYTE* copyFrom = lowPrefix;\n                        while (op < endOfMatch) { *op++ = *copyFrom++; }\n                    } else {\n                        LZ4_memcpy(op, lowPrefix, restSize);\n                        op += restSize;\n                }   }\n                continue;\n            }\n\n            /* copy match within block */\n            cpy = op + length;\n\n            assert((op <= oend) && (oend-op >= 32));\n            if (unlikely(offset<16)) {\n                LZ4_memcpy_using_offset(op, match, cpy, offset);\n            } else {\n                LZ4_wildCopy32(op, match, cpy);\n            }\n\n            op = cpy;   /* wildcopy correction */\n        }\n    safe_decode:\n#endif\n\n        /* Main Loop : decode remaining sequences where output < FASTLOOP_SAFE_DISTANCE */\n        DEBUGLOG(6, \"using safe decode loop\");\n        while (1) {\n            assert(ip < iend);\n            token = *ip++;\n            length = token >> ML_BITS;  /* literal length */\n\n            /* A two-stage shortcut for the most common case:\n             * 1) If the literal length is 0..14, and there is enough space,\n             * enter the shortcut and copy 16 bytes on behalf of the literals\n             * (in the fast mode, only 8 bytes can be safely copied this way).\n             * 2) Further if the match length is 4..18, copy 18 bytes in a similar\n             * manner; but we ensure that there's enough space in the output for\n             * those 18 bytes earlier, upon entering the shortcut (in other words,\n             * there is a combined check for both stages).\n             */\n            if ( (length != RUN_MASK)\n                /* strictly \"less than\" on input, to re-enter the loop with at least one byte */\n              && likely((ip < shortiend) & (op <= shortoend)) ) {\n                /* Copy the literals */\n                LZ4_memcpy(op, ip, 16);\n                op += length; ip += length;\n\n                /* The second stage: prepare for match copying, decode full info.\n                 * If it doesn't work out, the info won't be wasted. */\n                length = token & ML_MASK; /* match length */\n                offset = LZ4_readLE16(ip); ip += 2;\n                match = op - offset;\n                assert(match <= op); /* check overflow */\n\n                /* Do not deal with overlapping matches. */\n                if ( (length != ML_MASK)\n                  && (offset >= 8)\n                  && (dict==withPrefix64k || match >= lowPrefix) ) {\n                    /* Copy the match. */\n                    LZ4_memcpy(op + 0, match + 0, 8);\n                    LZ4_memcpy(op + 8, match + 8, 8);\n                    LZ4_memcpy(op +16, match +16, 2);\n                    op += length + MINMATCH;\n                    /* Both stages worked, load the next token. */\n                    continue;\n                }\n\n                /* The second stage didn't work out, but the info is ready.\n                 * Propel it right to the point of match copying. */\n                goto _copy_match;\n            }\n\n            /* decode literal length */\n            if (length == RUN_MASK) {\n                size_t const addl = read_variable_length(&ip, iend-RUN_MASK, 1);\n                if (addl == rvl_error) { goto _output_error; }\n                length += addl;\n                if (unlikely((uptrval)(op)+length<(uptrval)(op))) { goto _output_error; } /* overflow detection */\n                if (unlikely((uptrval)(ip)+length<(uptrval)(ip))) { goto _output_error; } /* overflow detection */\n            }\n\n            /* copy literals */\n            cpy = op+length;\n#if LZ4_FAST_DEC_LOOP\n        safe_literal_copy:\n#endif\n            LZ4_STATIC_ASSERT(MFLIMIT >= WILDCOPYLENGTH);\n            if ((cpy>oend-MFLIMIT) || (ip+length>iend-(2+1+LASTLITERALS))) {\n                /* We've either hit the input parsing restriction or the output parsing restriction.\n                 * In the normal scenario, decoding a full block, it must be the last sequence,\n                 * otherwise it's an error (invalid input or dimensions).\n                 * In partialDecoding scenario, it's necessary to ensure there is no buffer overflow.\n                 */\n                if (partialDecoding) {\n                    /* Since we are partial decoding we may be in this block because of the output parsing\n                     * restriction, which is not valid since the output buffer is allowed to be undersized.\n                     */\n                    DEBUGLOG(7, \"partialDecoding: copying literals, close to input or output end\")\n                    DEBUGLOG(7, \"partialDecoding: literal length = %u\", (unsigned)length);\n                    DEBUGLOG(7, \"partialDecoding: remaining space in dstBuffer : %i\", (int)(oend - op));\n                    DEBUGLOG(7, \"partialDecoding: remaining space in srcBuffer : %i\", (int)(iend - ip));\n                    /* Finishing in the middle of a literals segment,\n                     * due to lack of input.\n                     */\n                    if (ip+length > iend) {\n                        length = (size_t)(iend-ip);\n                        cpy = op + length;\n                    }\n                    /* Finishing in the middle of a literals segment,\n                     * due to lack of output space.\n                     */\n                    if (cpy > oend) {\n                        cpy = oend;\n                        assert(op<=oend);\n                        length = (size_t)(oend-op);\n                    }\n                } else {\n                     /* We must be on the last sequence (or invalid) because of the parsing limitations\n                      * so check that we exactly consume the input and don't overrun the output buffer.\n                      */\n                    if ((ip+length != iend) || (cpy > oend)) {\n                        DEBUGLOG(6, \"should have been last run of literals\")\n                        DEBUGLOG(6, \"ip(%p) + length(%i) = %p != iend (%p)\", ip, (int)length, ip+length, iend);\n                        DEBUGLOG(6, \"or cpy(%p) > oend(%p)\", cpy, oend);\n                        goto _output_error;\n                    }\n                }\n                LZ4_memmove(op, ip, length);  /* supports overlapping memory regions, for in-place decompression scenarios */\n                ip += length;\n                op += length;\n                /* Necessarily EOF when !partialDecoding.\n                 * When partialDecoding, it is EOF if we've either\n                 * filled the output buffer or\n                 * can't proceed with reading an offset for following match.\n                 */\n                if (!partialDecoding || (cpy == oend) || (ip >= (iend-2))) {\n                    break;\n                }\n            } else {\n                LZ4_wildCopy8(op, ip, cpy);   /* can overwrite up to 8 bytes beyond cpy */\n                ip += length; op = cpy;\n            }\n\n            /* get offset */\n            offset = LZ4_readLE16(ip); ip+=2;\n            match = op - offset;\n\n            /* get matchlength */\n            length = token & ML_MASK;\n\n    _copy_match:\n            if (length == ML_MASK) {\n                size_t const addl = read_variable_length(&ip, iend - LASTLITERALS + 1, 0);\n                if (addl == rvl_error) { goto _output_error; }\n                length += addl;\n                if (unlikely((uptrval)(op)+length<(uptrval)op)) goto _output_error;   /* overflow detection */\n            }\n            length += MINMATCH;\n\n#if LZ4_FAST_DEC_LOOP\n        safe_match_copy:\n#endif\n            if ((checkOffset) && (unlikely(match + dictSize < lowPrefix))) goto _output_error;   /* Error : offset outside buffers */\n            /* match starting within external dictionary */\n            if ((dict==usingExtDict) && (match < lowPrefix)) {\n                assert(dictEnd != NULL);\n                if (unlikely(op+length > oend-LASTLITERALS)) {\n                    if (partialDecoding) length = MIN(length, (size_t)(oend-op));\n                    else goto _output_error;   /* doesn't respect parsing restriction */\n                }\n\n                if (length <= (size_t)(lowPrefix-match)) {\n                    /* match fits entirely within external dictionary : just copy */\n                    LZ4_memmove(op, dictEnd - (lowPrefix-match), length);\n                    op += length;\n                } else {\n                    /* match stretches into both external dictionary and current block */\n                    size_t const copySize = (size_t)(lowPrefix - match);\n                    size_t const restSize = length - copySize;\n                    LZ4_memcpy(op, dictEnd - copySize, copySize);\n                    op += copySize;\n                    if (restSize > (size_t)(op - lowPrefix)) {  /* overlap copy */\n                        BYTE* const endOfMatch = op + restSize;\n                        const BYTE* copyFrom = lowPrefix;\n                        while (op < endOfMatch) *op++ = *copyFrom++;\n                    } else {\n                        LZ4_memcpy(op, lowPrefix, restSize);\n                        op += restSize;\n                }   }\n                continue;\n            }\n            assert(match >= lowPrefix);\n\n            /* copy match within block */\n            cpy = op + length;\n\n            /* partialDecoding : may end anywhere within the block */\n            assert(op<=oend);\n            if (partialDecoding && (cpy > oend-MATCH_SAFEGUARD_DISTANCE)) {\n                size_t const mlen = MIN(length, (size_t)(oend-op));\n                const BYTE* const matchEnd = match + mlen;\n                BYTE* const copyEnd = op + mlen;\n                if (matchEnd > op) {   /* overlap copy */\n                    while (op < copyEnd) { *op++ = *match++; }\n                } else {\n                    LZ4_memcpy(op, match, mlen);\n                }\n                op = copyEnd;\n                if (op == oend) { break; }\n                continue;\n            }\n\n            if (unlikely(offset<8)) {\n                LZ4_write32(op, 0);   /* silence msan warning when offset==0 */\n                op[0] = match[0];\n                op[1] = match[1];\n                op[2] = match[2];\n                op[3] = match[3];\n                match += inc32table[offset];\n                LZ4_memcpy(op+4, match, 4);\n                match -= dec64table[offset];\n            } else {\n                LZ4_memcpy(op, match, 8);\n                match += 8;\n            }\n            op += 8;\n\n            if (unlikely(cpy > oend-MATCH_SAFEGUARD_DISTANCE)) {\n                BYTE* const oCopyLimit = oend - (WILDCOPYLENGTH-1);\n                if (cpy > oend-LASTLITERALS) { goto _output_error; } /* Error : last LASTLITERALS bytes must be literals (uncompressed) */\n                if (op < oCopyLimit) {\n                    LZ4_wildCopy8(op, match, oCopyLimit);\n                    match += oCopyLimit - op;\n                    op = oCopyLimit;\n                }\n                while (op < cpy) { *op++ = *match++; }\n            } else {\n                LZ4_memcpy(op, match, 8);\n                if (length > 16)  { LZ4_wildCopy8(op+8, match+8, cpy); }\n            }\n            op = cpy;   /* wildcopy correction */\n        }\n\n        /* end of decoding */\n        DEBUGLOG(5, \"decoded %i bytes\", (int) (((char*)op)-dst));\n        return (int) (((char*)op)-dst);     /* Nb of output bytes decoded */\n\n        /* Overflow error detected */\n    _output_error:\n        return (int) (-(((const char*)ip)-src))-1;\n    }\n}\n\n\n/*===== Instantiate the API decoding functions. =====*/\n\nLZ4_FORCE_O2\nint LZ4_decompress_safe(const char* source, char* dest, int compressedSize, int maxDecompressedSize)\n{\n    return LZ4_decompress_generic(source, dest, compressedSize, maxDecompressedSize,\n                                  decode_full_block, noDict,\n                                  (BYTE*)dest, NULL, 0);\n}\n\nLZ4_FORCE_O2\nint LZ4_decompress_safe_partial(const char* src, char* dst, int compressedSize, int targetOutputSize, int dstCapacity)\n{\n    dstCapacity = MIN(targetOutputSize, dstCapacity);\n    return LZ4_decompress_generic(src, dst, compressedSize, dstCapacity,\n                                  partial_decode,\n                                  noDict, (BYTE*)dst, NULL, 0);\n}\n\nLZ4_FORCE_O2\nint LZ4_decompress_fast(const char* source, char* dest, int originalSize)\n{\n    DEBUGLOG(5, \"LZ4_decompress_fast\");\n    return LZ4_decompress_unsafe_generic(\n                (const BYTE*)source, (BYTE*)dest, originalSize,\n                0, NULL, 0);\n}\n\n/*===== Instantiate a few more decoding cases, used more than once. =====*/\n\nLZ4_FORCE_O2 /* Exported, an obsolete API function. */\nint LZ4_decompress_safe_withPrefix64k(const char* source, char* dest, int compressedSize, int maxOutputSize)\n{\n    return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize,\n                                  decode_full_block, withPrefix64k,\n                                  (BYTE*)dest - 64 KB, NULL, 0);\n}\n\nLZ4_FORCE_O2\nstatic int LZ4_decompress_safe_partial_withPrefix64k(const char* source, char* dest, int compressedSize, int targetOutputSize, int dstCapacity)\n{\n    dstCapacity = MIN(targetOutputSize, dstCapacity);\n    return LZ4_decompress_generic(source, dest, compressedSize, dstCapacity,\n                                  partial_decode, withPrefix64k,\n                                  (BYTE*)dest - 64 KB, NULL, 0);\n}\n\n/* Another obsolete API function, paired with the previous one. */\nint LZ4_decompress_fast_withPrefix64k(const char* source, char* dest, int originalSize)\n{\n    return LZ4_decompress_unsafe_generic(\n                (const BYTE*)source, (BYTE*)dest, originalSize,\n                64 KB, NULL, 0);\n}\n\nLZ4_FORCE_O2\nstatic int LZ4_decompress_safe_withSmallPrefix(const char* source, char* dest, int compressedSize, int maxOutputSize,\n                                               size_t prefixSize)\n{\n    return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize,\n                                  decode_full_block, noDict,\n                                  (BYTE*)dest-prefixSize, NULL, 0);\n}\n\nLZ4_FORCE_O2\nstatic int LZ4_decompress_safe_partial_withSmallPrefix(const char* source, char* dest, int compressedSize, int targetOutputSize, int dstCapacity,\n                                               size_t prefixSize)\n{\n    dstCapacity = MIN(targetOutputSize, dstCapacity);\n    return LZ4_decompress_generic(source, dest, compressedSize, dstCapacity,\n                                  partial_decode, noDict,\n                                  (BYTE*)dest-prefixSize, NULL, 0);\n}\n\nLZ4_FORCE_O2\nint LZ4_decompress_safe_forceExtDict(const char* source, char* dest,\n                                     int compressedSize, int maxOutputSize,\n                                     const void* dictStart, size_t dictSize)\n{\n    DEBUGLOG(5, \"LZ4_decompress_safe_forceExtDict\");\n    return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize,\n                                  decode_full_block, usingExtDict,\n                                  (BYTE*)dest, (const BYTE*)dictStart, dictSize);\n}\n\nLZ4_FORCE_O2\nint LZ4_decompress_safe_partial_forceExtDict(const char* source, char* dest,\n                                     int compressedSize, int targetOutputSize, int dstCapacity,\n                                     const void* dictStart, size_t dictSize)\n{\n    dstCapacity = MIN(targetOutputSize, dstCapacity);\n    return LZ4_decompress_generic(source, dest, compressedSize, dstCapacity,\n                                  partial_decode, usingExtDict,\n                                  (BYTE*)dest, (const BYTE*)dictStart, dictSize);\n}\n\nLZ4_FORCE_O2\nstatic int LZ4_decompress_fast_extDict(const char* source, char* dest, int originalSize,\n                                       const void* dictStart, size_t dictSize)\n{\n    return LZ4_decompress_unsafe_generic(\n                (const BYTE*)source, (BYTE*)dest, originalSize,\n                0, (const BYTE*)dictStart, dictSize);\n}\n\n/* The \"double dictionary\" mode, for use with e.g. ring buffers: the first part\n * of the dictionary is passed as prefix, and the second via dictStart + dictSize.\n * These routines are used only once, in LZ4_decompress_*_continue().\n */\nLZ4_FORCE_INLINE\nint LZ4_decompress_safe_doubleDict(const char* source, char* dest, int compressedSize, int maxOutputSize,\n                                   size_t prefixSize, const void* dictStart, size_t dictSize)\n{\n    return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize,\n                                  decode_full_block, usingExtDict,\n                                  (BYTE*)dest-prefixSize, (const BYTE*)dictStart, dictSize);\n}\n\n/*===== streaming decompression functions =====*/\n\n#if !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION)\nLZ4_streamDecode_t* LZ4_createStreamDecode(void)\n{\n    LZ4_STATIC_ASSERT(sizeof(LZ4_streamDecode_t) >= sizeof(LZ4_streamDecode_t_internal));\n    return (LZ4_streamDecode_t*) ALLOC_AND_ZERO(sizeof(LZ4_streamDecode_t));\n}\n\nint LZ4_freeStreamDecode (LZ4_streamDecode_t* LZ4_stream)\n{\n    if (LZ4_stream == NULL) { return 0; }  /* support free on NULL */\n    FREEMEM(LZ4_stream);\n    return 0;\n}\n#endif\n\n/*! LZ4_setStreamDecode() :\n *  Use this function to instruct where to find the dictionary.\n *  This function is not necessary if previous data is still available where it was decoded.\n *  Loading a size of 0 is allowed (same effect as no dictionary).\n * @return : 1 if OK, 0 if error\n */\nint LZ4_setStreamDecode (LZ4_streamDecode_t* LZ4_streamDecode, const char* dictionary, int dictSize)\n{\n    LZ4_streamDecode_t_internal* lz4sd = &LZ4_streamDecode->internal_donotuse;\n    lz4sd->prefixSize = (size_t)dictSize;\n    if (dictSize) {\n        assert(dictionary != NULL);\n        lz4sd->prefixEnd = (const BYTE*) dictionary + dictSize;\n    } else {\n        lz4sd->prefixEnd = (const BYTE*) dictionary;\n    }\n    lz4sd->externalDict = NULL;\n    lz4sd->extDictSize  = 0;\n    return 1;\n}\n\n/*! LZ4_decoderRingBufferSize() :\n *  when setting a ring buffer for streaming decompression (optional scenario),\n *  provides the minimum size of this ring buffer\n *  to be compatible with any source respecting maxBlockSize condition.\n *  Note : in a ring buffer scenario,\n *  blocks are presumed decompressed next to each other.\n *  When not enough space remains for next block (remainingSize < maxBlockSize),\n *  decoding resumes from beginning of ring buffer.\n * @return : minimum ring buffer size,\n *           or 0 if there is an error (invalid maxBlockSize).\n */\nint LZ4_decoderRingBufferSize(int maxBlockSize)\n{\n    if (maxBlockSize < 0) return 0;\n    if (maxBlockSize > LZ4_MAX_INPUT_SIZE) return 0;\n    if (maxBlockSize < 16) maxBlockSize = 16;\n    return LZ4_DECODER_RING_BUFFER_SIZE(maxBlockSize);\n}\n\n/*\n*_continue() :\n    These decoding functions allow decompression of multiple blocks in \"streaming\" mode.\n    Previously decoded blocks must still be available at the memory position where they were decoded.\n    If it's not possible, save the relevant part of decoded data into a safe buffer,\n    and indicate where it stands using LZ4_setStreamDecode()\n*/\nLZ4_FORCE_O2\nint LZ4_decompress_safe_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* source, char* dest, int compressedSize, int maxOutputSize)\n{\n    LZ4_streamDecode_t_internal* lz4sd = &LZ4_streamDecode->internal_donotuse;\n    int result;\n\n    if (lz4sd->prefixSize == 0) {\n        /* The first call, no dictionary yet. */\n        assert(lz4sd->extDictSize == 0);\n        result = LZ4_decompress_safe(source, dest, compressedSize, maxOutputSize);\n        if (result <= 0) return result;\n        lz4sd->prefixSize = (size_t)result;\n        lz4sd->prefixEnd = (BYTE*)dest + result;\n    } else if (lz4sd->prefixEnd == (BYTE*)dest) {\n        /* They're rolling the current segment. */\n        if (lz4sd->prefixSize >= 64 KB - 1)\n            result = LZ4_decompress_safe_withPrefix64k(source, dest, compressedSize, maxOutputSize);\n        else if (lz4sd->extDictSize == 0)\n            result = LZ4_decompress_safe_withSmallPrefix(source, dest, compressedSize, maxOutputSize,\n                                                         lz4sd->prefixSize);\n        else\n            result = LZ4_decompress_safe_doubleDict(source, dest, compressedSize, maxOutputSize,\n                                                    lz4sd->prefixSize, lz4sd->externalDict, lz4sd->extDictSize);\n        if (result <= 0) return result;\n        lz4sd->prefixSize += (size_t)result;\n        lz4sd->prefixEnd  += result;\n    } else {\n        /* The buffer wraps around, or they're switching to another buffer. */\n        lz4sd->extDictSize = lz4sd->prefixSize;\n        lz4sd->externalDict = lz4sd->prefixEnd - lz4sd->extDictSize;\n        result = LZ4_decompress_safe_forceExtDict(source, dest, compressedSize, maxOutputSize,\n                                                  lz4sd->externalDict, lz4sd->extDictSize);\n        if (result <= 0) return result;\n        lz4sd->prefixSize = (size_t)result;\n        lz4sd->prefixEnd  = (BYTE*)dest + result;\n    }\n\n    return result;\n}\n\nLZ4_FORCE_O2 int\nLZ4_decompress_fast_continue (LZ4_streamDecode_t* LZ4_streamDecode,\n                        const char* source, char* dest, int originalSize)\n{\n    LZ4_streamDecode_t_internal* const lz4sd =\n        (assert(LZ4_streamDecode!=NULL), &LZ4_streamDecode->internal_donotuse);\n    int result;\n\n    DEBUGLOG(5, \"LZ4_decompress_fast_continue (toDecodeSize=%i)\", originalSize);\n    assert(originalSize >= 0);\n\n    if (lz4sd->prefixSize == 0) {\n        DEBUGLOG(5, \"first invocation : no prefix nor extDict\");\n        assert(lz4sd->extDictSize == 0);\n        result = LZ4_decompress_fast(source, dest, originalSize);\n        if (result <= 0) return result;\n        lz4sd->prefixSize = (size_t)originalSize;\n        lz4sd->prefixEnd = (BYTE*)dest + originalSize;\n    } else if (lz4sd->prefixEnd == (BYTE*)dest) {\n        DEBUGLOG(5, \"continue using existing prefix\");\n        result = LZ4_decompress_unsafe_generic(\n                        (const BYTE*)source, (BYTE*)dest, originalSize,\n                        lz4sd->prefixSize,\n                        lz4sd->externalDict, lz4sd->extDictSize);\n        if (result <= 0) return result;\n        lz4sd->prefixSize += (size_t)originalSize;\n        lz4sd->prefixEnd  += originalSize;\n    } else {\n        DEBUGLOG(5, \"prefix becomes extDict\");\n        lz4sd->extDictSize = lz4sd->prefixSize;\n        lz4sd->externalDict = lz4sd->prefixEnd - lz4sd->extDictSize;\n        result = LZ4_decompress_fast_extDict(source, dest, originalSize,\n                                             lz4sd->externalDict, lz4sd->extDictSize);\n        if (result <= 0) return result;\n        lz4sd->prefixSize = (size_t)originalSize;\n        lz4sd->prefixEnd  = (BYTE*)dest + originalSize;\n    }\n\n    return result;\n}\n\n\n/*\nAdvanced decoding functions :\n*_usingDict() :\n    These decoding functions work the same as \"_continue\" ones,\n    the dictionary must be explicitly provided within parameters\n*/\n\nint LZ4_decompress_safe_usingDict(const char* source, char* dest, int compressedSize, int maxOutputSize, const char* dictStart, int dictSize)\n{\n    if (dictSize==0)\n        return LZ4_decompress_safe(source, dest, compressedSize, maxOutputSize);\n    if (dictStart+dictSize == dest) {\n        if (dictSize >= 64 KB - 1) {\n            return LZ4_decompress_safe_withPrefix64k(source, dest, compressedSize, maxOutputSize);\n        }\n        assert(dictSize >= 0);\n        return LZ4_decompress_safe_withSmallPrefix(source, dest, compressedSize, maxOutputSize, (size_t)dictSize);\n    }\n    assert(dictSize >= 0);\n    return LZ4_decompress_safe_forceExtDict(source, dest, compressedSize, maxOutputSize, dictStart, (size_t)dictSize);\n}\n\nint LZ4_decompress_safe_partial_usingDict(const char* source, char* dest, int compressedSize, int targetOutputSize, int dstCapacity, const char* dictStart, int dictSize)\n{\n    if (dictSize==0)\n        return LZ4_decompress_safe_partial(source, dest, compressedSize, targetOutputSize, dstCapacity);\n    if (dictStart+dictSize == dest) {\n        if (dictSize >= 64 KB - 1) {\n            return LZ4_decompress_safe_partial_withPrefix64k(source, dest, compressedSize, targetOutputSize, dstCapacity);\n        }\n        assert(dictSize >= 0);\n        return LZ4_decompress_safe_partial_withSmallPrefix(source, dest, compressedSize, targetOutputSize, dstCapacity, (size_t)dictSize);\n    }\n    assert(dictSize >= 0);\n    return LZ4_decompress_safe_partial_forceExtDict(source, dest, compressedSize, targetOutputSize, dstCapacity, dictStart, (size_t)dictSize);\n}\n\nint LZ4_decompress_fast_usingDict(const char* source, char* dest, int originalSize, const char* dictStart, int dictSize)\n{\n    if (dictSize==0 || dictStart+dictSize == dest)\n        return LZ4_decompress_unsafe_generic(\n                        (const BYTE*)source, (BYTE*)dest, originalSize,\n                        (size_t)dictSize, NULL, 0);\n    assert(dictSize >= 0);\n    return LZ4_decompress_fast_extDict(source, dest, originalSize, dictStart, (size_t)dictSize);\n}\n\n\n/*=*************************************************\n*  Obsolete Functions\n***************************************************/\n/* obsolete compression functions */\nint LZ4_compress_limitedOutput(const char* source, char* dest, int inputSize, int maxOutputSize)\n{\n    return LZ4_compress_default(source, dest, inputSize, maxOutputSize);\n}\nint LZ4_compress(const char* src, char* dest, int srcSize)\n{\n    return LZ4_compress_default(src, dest, srcSize, LZ4_compressBound(srcSize));\n}\nint LZ4_compress_limitedOutput_withState (void* state, const char* src, char* dst, int srcSize, int dstSize)\n{\n    return LZ4_compress_fast_extState(state, src, dst, srcSize, dstSize, 1);\n}\nint LZ4_compress_withState (void* state, const char* src, char* dst, int srcSize)\n{\n    return LZ4_compress_fast_extState(state, src, dst, srcSize, LZ4_compressBound(srcSize), 1);\n}\nint LZ4_compress_limitedOutput_continue (LZ4_stream_t* LZ4_stream, const char* src, char* dst, int srcSize, int dstCapacity)\n{\n    return LZ4_compress_fast_continue(LZ4_stream, src, dst, srcSize, dstCapacity, 1);\n}\nint LZ4_compress_continue (LZ4_stream_t* LZ4_stream, const char* source, char* dest, int inputSize)\n{\n    return LZ4_compress_fast_continue(LZ4_stream, source, dest, inputSize, LZ4_compressBound(inputSize), 1);\n}\n\n/*\nThese decompression functions are deprecated and should no longer be used.\nThey are only provided here for compatibility with older user programs.\n- LZ4_uncompress is totally equivalent to LZ4_decompress_fast\n- LZ4_uncompress_unknownOutputSize is totally equivalent to LZ4_decompress_safe\n*/\nint LZ4_uncompress (const char* source, char* dest, int outputSize)\n{\n    return LZ4_decompress_fast(source, dest, outputSize);\n}\nint LZ4_uncompress_unknownOutputSize (const char* source, char* dest, int isize, int maxOutputSize)\n{\n    return LZ4_decompress_safe(source, dest, isize, maxOutputSize);\n}\n\n/* Obsolete Streaming functions */\n\nint LZ4_sizeofStreamState(void) { return sizeof(LZ4_stream_t); }\n\nint LZ4_resetStreamState(void* state, char* inputBuffer)\n{\n    (void)inputBuffer;\n    LZ4_resetStream((LZ4_stream_t*)state);\n    return 0;\n}\n\n#if !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION)\nvoid* LZ4_create (char* inputBuffer)\n{\n    (void)inputBuffer;\n    return LZ4_createStream();\n}\n#endif\n\nchar* LZ4_slideInputBuffer (void* state)\n{\n    /* avoid const char * -> char * conversion warning */\n    return (char *)(uptrval)((LZ4_stream_t*)state)->internal_donotuse.dictionary;\n}\n\n#endif   /* LZ4_COMMONDEFS_ONLY */\n"
  },
  {
    "path": "jni/lz4/lz4.h",
    "content": "/*\n *  LZ4 - Fast LZ compression algorithm\n *  Header File\n *  Copyright (C) 2011-2020, Yann Collet.\n\n   BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)\n\n   Redistribution and use in source and binary forms, with or without\n   modification, are permitted provided that the following conditions are\n   met:\n\n       * Redistributions of source code must retain the above copyright\n   notice, this list of conditions and the following disclaimer.\n       * Redistributions in binary form must reproduce the above\n   copyright notice, this list of conditions and the following disclaimer\n   in the documentation and/or other materials provided with the\n   distribution.\n\n   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n   \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n   You can contact the author at :\n    - LZ4 homepage : http://www.lz4.org\n    - LZ4 source repository : https://github.com/lz4/lz4\n*/\n#if defined (__cplusplus)\nextern \"C\" {\n#endif\n\n#ifndef LZ4_H_2983827168210\n#define LZ4_H_2983827168210\n\n/* --- Dependency --- */\n#include <stddef.h>   /* size_t */\n\n\n/**\n  Introduction\n\n  LZ4 is lossless compression algorithm, providing compression speed >500 MB/s per core,\n  scalable with multi-cores CPU. It features an extremely fast decoder, with speed in\n  multiple GB/s per core, typically reaching RAM speed limits on multi-core systems.\n\n  The LZ4 compression library provides in-memory compression and decompression functions.\n  It gives full buffer control to user.\n  Compression can be done in:\n    - a single step (described as Simple Functions)\n    - a single step, reusing a context (described in Advanced Functions)\n    - unbounded multiple steps (described as Streaming compression)\n\n  lz4.h generates and decodes LZ4-compressed blocks (doc/lz4_Block_format.md).\n  Decompressing such a compressed block requires additional metadata.\n  Exact metadata depends on exact decompression function.\n  For the typical case of LZ4_decompress_safe(),\n  metadata includes block's compressed size, and maximum bound of decompressed size.\n  Each application is free to encode and pass such metadata in whichever way it wants.\n\n  lz4.h only handle blocks, it can not generate Frames.\n\n  Blocks are different from Frames (doc/lz4_Frame_format.md).\n  Frames bundle both blocks and metadata in a specified manner.\n  Embedding metadata is required for compressed data to be self-contained and portable.\n  Frame format is delivered through a companion API, declared in lz4frame.h.\n  The `lz4` CLI can only manage frames.\n*/\n\n/*^***************************************************************\n*  Export parameters\n*****************************************************************/\n/*\n*  LZ4_DLL_EXPORT :\n*  Enable exporting of functions when building a Windows DLL\n*  LZ4LIB_VISIBILITY :\n*  Control library symbols visibility.\n*/\n#ifndef LZ4LIB_VISIBILITY\n#  if defined(__GNUC__) && (__GNUC__ >= 4)\n#    define LZ4LIB_VISIBILITY __attribute__ ((visibility (\"default\")))\n#  else\n#    define LZ4LIB_VISIBILITY\n#  endif\n#endif\n#if defined(LZ4_DLL_EXPORT) && (LZ4_DLL_EXPORT==1)\n#  define LZ4LIB_API __declspec(dllexport) LZ4LIB_VISIBILITY\n#elif defined(LZ4_DLL_IMPORT) && (LZ4_DLL_IMPORT==1)\n#  define LZ4LIB_API __declspec(dllimport) LZ4LIB_VISIBILITY /* It isn't required but allows to generate better code, saving a function pointer load from the IAT and an indirect jump.*/\n#else\n#  define LZ4LIB_API LZ4LIB_VISIBILITY\n#endif\n\n/*! LZ4_FREESTANDING :\n *  When this macro is set to 1, it enables \"freestanding mode\" that is\n *  suitable for typical freestanding environment which doesn't support\n *  standard C library.\n *\n *  - LZ4_FREESTANDING is a compile-time switch.\n *  - It requires the following macros to be defined:\n *    LZ4_memcpy, LZ4_memmove, LZ4_memset.\n *  - It only enables LZ4/HC functions which don't use heap.\n *    All LZ4F_* functions are not supported.\n *  - See tests/freestanding.c to check its basic setup.\n */\n#if defined(LZ4_FREESTANDING) && (LZ4_FREESTANDING == 1)\n#  define LZ4_HEAPMODE 0\n#  define LZ4HC_HEAPMODE 0\n#  define LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION 1\n#  if !defined(LZ4_memcpy)\n#    error \"LZ4_FREESTANDING requires macro 'LZ4_memcpy'.\"\n#  endif\n#  if !defined(LZ4_memset)\n#    error \"LZ4_FREESTANDING requires macro 'LZ4_memset'.\"\n#  endif\n#  if !defined(LZ4_memmove)\n#    error \"LZ4_FREESTANDING requires macro 'LZ4_memmove'.\"\n#  endif\n#elif ! defined(LZ4_FREESTANDING)\n#  define LZ4_FREESTANDING 0\n#endif\n\n\n/*------   Version   ------*/\n#define LZ4_VERSION_MAJOR    1    /* for breaking interface changes  */\n#define LZ4_VERSION_MINOR    9    /* for new (non-breaking) interface capabilities */\n#define LZ4_VERSION_RELEASE  4    /* for tweaks, bug-fixes, or development */\n\n#define LZ4_VERSION_NUMBER (LZ4_VERSION_MAJOR *100*100 + LZ4_VERSION_MINOR *100 + LZ4_VERSION_RELEASE)\n\n#define LZ4_LIB_VERSION LZ4_VERSION_MAJOR.LZ4_VERSION_MINOR.LZ4_VERSION_RELEASE\n#define LZ4_QUOTE(str) #str\n#define LZ4_EXPAND_AND_QUOTE(str) LZ4_QUOTE(str)\n#define LZ4_VERSION_STRING LZ4_EXPAND_AND_QUOTE(LZ4_LIB_VERSION)  /* requires v1.7.3+ */\n\nLZ4LIB_API int LZ4_versionNumber (void);  /**< library version number; useful to check dll version; requires v1.3.0+ */\nLZ4LIB_API const char* LZ4_versionString (void);   /**< library version string; useful to check dll version; requires v1.7.5+ */\n\n\n/*-************************************\n*  Tuning parameter\n**************************************/\n#define LZ4_MEMORY_USAGE_MIN 10\n#define LZ4_MEMORY_USAGE_DEFAULT 14\n#define LZ4_MEMORY_USAGE_MAX 20\n\n/*!\n * LZ4_MEMORY_USAGE :\n * Memory usage formula : N->2^N Bytes (examples : 10 -> 1KB; 12 -> 4KB ; 16 -> 64KB; 20 -> 1MB; )\n * Increasing memory usage improves compression ratio, at the cost of speed.\n * Reduced memory usage may improve speed at the cost of ratio, thanks to better cache locality.\n * Default value is 14, for 16KB, which nicely fits into Intel x86 L1 cache\n */\n#ifndef LZ4_MEMORY_USAGE\n# define LZ4_MEMORY_USAGE LZ4_MEMORY_USAGE_DEFAULT\n#endif\n\n#if (LZ4_MEMORY_USAGE < LZ4_MEMORY_USAGE_MIN)\n#  error \"LZ4_MEMORY_USAGE is too small !\"\n#endif\n\n#if (LZ4_MEMORY_USAGE > LZ4_MEMORY_USAGE_MAX)\n#  error \"LZ4_MEMORY_USAGE is too large !\"\n#endif\n\n/*-************************************\n*  Simple Functions\n**************************************/\n/*! LZ4_compress_default() :\n *  Compresses 'srcSize' bytes from buffer 'src'\n *  into already allocated 'dst' buffer of size 'dstCapacity'.\n *  Compression is guaranteed to succeed if 'dstCapacity' >= LZ4_compressBound(srcSize).\n *  It also runs faster, so it's a recommended setting.\n *  If the function cannot compress 'src' into a more limited 'dst' budget,\n *  compression stops *immediately*, and the function result is zero.\n *  In which case, 'dst' content is undefined (invalid).\n *      srcSize : max supported value is LZ4_MAX_INPUT_SIZE.\n *      dstCapacity : size of buffer 'dst' (which must be already allocated)\n *     @return  : the number of bytes written into buffer 'dst' (necessarily <= dstCapacity)\n *                or 0 if compression fails\n * Note : This function is protected against buffer overflow scenarios (never writes outside 'dst' buffer, nor read outside 'source' buffer).\n */\nLZ4LIB_API int LZ4_compress_default(const char* src, char* dst, int srcSize, int dstCapacity);\n\n/*! LZ4_decompress_safe() :\n * @compressedSize : is the exact complete size of the compressed block.\n * @dstCapacity : is the size of destination buffer (which must be already allocated),\n *                is an upper bound of decompressed size.\n * @return : the number of bytes decompressed into destination buffer (necessarily <= dstCapacity)\n *           If destination buffer is not large enough, decoding will stop and output an error code (negative value).\n *           If the source stream is detected malformed, the function will stop decoding and return a negative result.\n * Note 1 : This function is protected against malicious data packets :\n *          it will never writes outside 'dst' buffer, nor read outside 'source' buffer,\n *          even if the compressed block is maliciously modified to order the decoder to do these actions.\n *          In such case, the decoder stops immediately, and considers the compressed block malformed.\n * Note 2 : compressedSize and dstCapacity must be provided to the function, the compressed block does not contain them.\n *          The implementation is free to send / store / derive this information in whichever way is most beneficial.\n *          If there is a need for a different format which bundles together both compressed data and its metadata, consider looking at lz4frame.h instead.\n */\nLZ4LIB_API int LZ4_decompress_safe (const char* src, char* dst, int compressedSize, int dstCapacity);\n\n\n/*-************************************\n*  Advanced Functions\n**************************************/\n#define LZ4_MAX_INPUT_SIZE        0x7E000000   /* 2 113 929 216 bytes */\n#define LZ4_COMPRESSBOUND(isize)  ((unsigned)(isize) > (unsigned)LZ4_MAX_INPUT_SIZE ? 0 : (isize) + ((isize)/255) + 16)\n\n/*! LZ4_compressBound() :\n    Provides the maximum size that LZ4 compression may output in a \"worst case\" scenario (input data not compressible)\n    This function is primarily useful for memory allocation purposes (destination buffer size).\n    Macro LZ4_COMPRESSBOUND() is also provided for compilation-time evaluation (stack memory allocation for example).\n    Note that LZ4_compress_default() compresses faster when dstCapacity is >= LZ4_compressBound(srcSize)\n        inputSize  : max supported value is LZ4_MAX_INPUT_SIZE\n        return : maximum output size in a \"worst case\" scenario\n              or 0, if input size is incorrect (too large or negative)\n*/\nLZ4LIB_API int LZ4_compressBound(int inputSize);\n\n/*! LZ4_compress_fast() :\n    Same as LZ4_compress_default(), but allows selection of \"acceleration\" factor.\n    The larger the acceleration value, the faster the algorithm, but also the lesser the compression.\n    It's a trade-off. It can be fine tuned, with each successive value providing roughly +~3% to speed.\n    An acceleration value of \"1\" is the same as regular LZ4_compress_default()\n    Values <= 0 will be replaced by LZ4_ACCELERATION_DEFAULT (currently == 1, see lz4.c).\n    Values > LZ4_ACCELERATION_MAX will be replaced by LZ4_ACCELERATION_MAX (currently == 65537, see lz4.c).\n*/\nLZ4LIB_API int LZ4_compress_fast (const char* src, char* dst, int srcSize, int dstCapacity, int acceleration);\n\n\n/*! LZ4_compress_fast_extState() :\n *  Same as LZ4_compress_fast(), using an externally allocated memory space for its state.\n *  Use LZ4_sizeofState() to know how much memory must be allocated,\n *  and allocate it on 8-bytes boundaries (using `malloc()` typically).\n *  Then, provide this buffer as `void* state` to compression function.\n */\nLZ4LIB_API int LZ4_sizeofState(void);\nLZ4LIB_API int LZ4_compress_fast_extState (void* state, const char* src, char* dst, int srcSize, int dstCapacity, int acceleration);\n\n\n/*! LZ4_compress_destSize() :\n *  Reverse the logic : compresses as much data as possible from 'src' buffer\n *  into already allocated buffer 'dst', of size >= 'targetDestSize'.\n *  This function either compresses the entire 'src' content into 'dst' if it's large enough,\n *  or fill 'dst' buffer completely with as much data as possible from 'src'.\n *  note: acceleration parameter is fixed to \"default\".\n *\n * *srcSizePtr : will be modified to indicate how many bytes where read from 'src' to fill 'dst'.\n *               New value is necessarily <= input value.\n * @return : Nb bytes written into 'dst' (necessarily <= targetDestSize)\n *           or 0 if compression fails.\n *\n * Note : from v1.8.2 to v1.9.1, this function had a bug (fixed un v1.9.2+):\n *        the produced compressed content could, in specific circumstances,\n *        require to be decompressed into a destination buffer larger\n *        by at least 1 byte than the content to decompress.\n *        If an application uses `LZ4_compress_destSize()`,\n *        it's highly recommended to update liblz4 to v1.9.2 or better.\n *        If this can't be done or ensured,\n *        the receiving decompression function should provide\n *        a dstCapacity which is > decompressedSize, by at least 1 byte.\n *        See https://github.com/lz4/lz4/issues/859 for details\n */\nLZ4LIB_API int LZ4_compress_destSize (const char* src, char* dst, int* srcSizePtr, int targetDstSize);\n\n\n/*! LZ4_decompress_safe_partial() :\n *  Decompress an LZ4 compressed block, of size 'srcSize' at position 'src',\n *  into destination buffer 'dst' of size 'dstCapacity'.\n *  Up to 'targetOutputSize' bytes will be decoded.\n *  The function stops decoding on reaching this objective.\n *  This can be useful to boost performance\n *  whenever only the beginning of a block is required.\n *\n * @return : the number of bytes decoded in `dst` (necessarily <= targetOutputSize)\n *           If source stream is detected malformed, function returns a negative result.\n *\n *  Note 1 : @return can be < targetOutputSize, if compressed block contains less data.\n *\n *  Note 2 : targetOutputSize must be <= dstCapacity\n *\n *  Note 3 : this function effectively stops decoding on reaching targetOutputSize,\n *           so dstCapacity is kind of redundant.\n *           This is because in older versions of this function,\n *           decoding operation would still write complete sequences.\n *           Therefore, there was no guarantee that it would stop writing at exactly targetOutputSize,\n *           it could write more bytes, though only up to dstCapacity.\n *           Some \"margin\" used to be required for this operation to work properly.\n *           Thankfully, this is no longer necessary.\n *           The function nonetheless keeps the same signature, in an effort to preserve API compatibility.\n *\n *  Note 4 : If srcSize is the exact size of the block,\n *           then targetOutputSize can be any value,\n *           including larger than the block's decompressed size.\n *           The function will, at most, generate block's decompressed size.\n *\n *  Note 5 : If srcSize is _larger_ than block's compressed size,\n *           then targetOutputSize **MUST** be <= block's decompressed size.\n *           Otherwise, *silent corruption will occur*.\n */\nLZ4LIB_API int LZ4_decompress_safe_partial (const char* src, char* dst, int srcSize, int targetOutputSize, int dstCapacity);\n\n\n/*-*********************************************\n*  Streaming Compression Functions\n***********************************************/\ntypedef union LZ4_stream_u LZ4_stream_t;  /* incomplete type (defined later) */\n\n/**\n Note about RC_INVOKED\n\n - RC_INVOKED is predefined symbol of rc.exe (the resource compiler which is part of MSVC/Visual Studio).\n   https://docs.microsoft.com/en-us/windows/win32/menurc/predefined-macros\n\n - Since rc.exe is a legacy compiler, it truncates long symbol (> 30 chars)\n   and reports warning \"RC4011: identifier truncated\".\n\n - To eliminate the warning, we surround long preprocessor symbol with\n   \"#if !defined(RC_INVOKED) ... #endif\" block that means\n   \"skip this block when rc.exe is trying to read it\".\n*/\n#if !defined(RC_INVOKED) /* https://docs.microsoft.com/en-us/windows/win32/menurc/predefined-macros */\n#if !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION)\nLZ4LIB_API LZ4_stream_t* LZ4_createStream(void);\nLZ4LIB_API int           LZ4_freeStream (LZ4_stream_t* streamPtr);\n#endif /* !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION) */\n#endif\n\n/*! LZ4_resetStream_fast() : v1.9.0+\n *  Use this to prepare an LZ4_stream_t for a new chain of dependent blocks\n *  (e.g., LZ4_compress_fast_continue()).\n *\n *  An LZ4_stream_t must be initialized once before usage.\n *  This is automatically done when created by LZ4_createStream().\n *  However, should the LZ4_stream_t be simply declared on stack (for example),\n *  it's necessary to initialize it first, using LZ4_initStream().\n *\n *  After init, start any new stream with LZ4_resetStream_fast().\n *  A same LZ4_stream_t can be re-used multiple times consecutively\n *  and compress multiple streams,\n *  provided that it starts each new stream with LZ4_resetStream_fast().\n *\n *  LZ4_resetStream_fast() is much faster than LZ4_initStream(),\n *  but is not compatible with memory regions containing garbage data.\n *\n *  Note: it's only useful to call LZ4_resetStream_fast()\n *        in the context of streaming compression.\n *        The *extState* functions perform their own resets.\n *        Invoking LZ4_resetStream_fast() before is redundant, and even counterproductive.\n */\nLZ4LIB_API void LZ4_resetStream_fast (LZ4_stream_t* streamPtr);\n\n/*! LZ4_loadDict() :\n *  Use this function to reference a static dictionary into LZ4_stream_t.\n *  The dictionary must remain available during compression.\n *  LZ4_loadDict() triggers a reset, so any previous data will be forgotten.\n *  The same dictionary will have to be loaded on decompression side for successful decoding.\n *  Dictionary are useful for better compression of small data (KB range).\n *  While LZ4 accept any input as dictionary,\n *  results are generally better when using Zstandard's Dictionary Builder.\n *  Loading a size of 0 is allowed, and is the same as reset.\n * @return : loaded dictionary size, in bytes (necessarily <= 64 KB)\n */\nLZ4LIB_API int LZ4_loadDict (LZ4_stream_t* streamPtr, const char* dictionary, int dictSize);\n\n/*! LZ4_compress_fast_continue() :\n *  Compress 'src' content using data from previously compressed blocks, for better compression ratio.\n * 'dst' buffer must be already allocated.\n *  If dstCapacity >= LZ4_compressBound(srcSize), compression is guaranteed to succeed, and runs faster.\n *\n * @return : size of compressed block\n *           or 0 if there is an error (typically, cannot fit into 'dst').\n *\n *  Note 1 : Each invocation to LZ4_compress_fast_continue() generates a new block.\n *           Each block has precise boundaries.\n *           Each block must be decompressed separately, calling LZ4_decompress_*() with relevant metadata.\n *           It's not possible to append blocks together and expect a single invocation of LZ4_decompress_*() to decompress them together.\n *\n *  Note 2 : The previous 64KB of source data is __assumed__ to remain present, unmodified, at same address in memory !\n *\n *  Note 3 : When input is structured as a double-buffer, each buffer can have any size, including < 64 KB.\n *           Make sure that buffers are separated, by at least one byte.\n *           This construction ensures that each block only depends on previous block.\n *\n *  Note 4 : If input buffer is a ring-buffer, it can have any size, including < 64 KB.\n *\n *  Note 5 : After an error, the stream status is undefined (invalid), it can only be reset or freed.\n */\nLZ4LIB_API int LZ4_compress_fast_continue (LZ4_stream_t* streamPtr, const char* src, char* dst, int srcSize, int dstCapacity, int acceleration);\n\n/*! LZ4_saveDict() :\n *  If last 64KB data cannot be guaranteed to remain available at its current memory location,\n *  save it into a safer place (char* safeBuffer).\n *  This is schematically equivalent to a memcpy() followed by LZ4_loadDict(),\n *  but is much faster, because LZ4_saveDict() doesn't need to rebuild tables.\n * @return : saved dictionary size in bytes (necessarily <= maxDictSize), or 0 if error.\n */\nLZ4LIB_API int LZ4_saveDict (LZ4_stream_t* streamPtr, char* safeBuffer, int maxDictSize);\n\n\n/*-**********************************************\n*  Streaming Decompression Functions\n*  Bufferless synchronous API\n************************************************/\ntypedef union LZ4_streamDecode_u LZ4_streamDecode_t;   /* tracking context */\n\n/*! LZ4_createStreamDecode() and LZ4_freeStreamDecode() :\n *  creation / destruction of streaming decompression tracking context.\n *  A tracking context can be re-used multiple times.\n */\n#if !defined(RC_INVOKED) /* https://docs.microsoft.com/en-us/windows/win32/menurc/predefined-macros */\n#if !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION)\nLZ4LIB_API LZ4_streamDecode_t* LZ4_createStreamDecode(void);\nLZ4LIB_API int                 LZ4_freeStreamDecode (LZ4_streamDecode_t* LZ4_stream);\n#endif /* !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION) */\n#endif\n\n/*! LZ4_setStreamDecode() :\n *  An LZ4_streamDecode_t context can be allocated once and re-used multiple times.\n *  Use this function to start decompression of a new stream of blocks.\n *  A dictionary can optionally be set. Use NULL or size 0 for a reset order.\n *  Dictionary is presumed stable : it must remain accessible and unmodified during next decompression.\n * @return : 1 if OK, 0 if error\n */\nLZ4LIB_API int LZ4_setStreamDecode (LZ4_streamDecode_t* LZ4_streamDecode, const char* dictionary, int dictSize);\n\n/*! LZ4_decoderRingBufferSize() : v1.8.2+\n *  Note : in a ring buffer scenario (optional),\n *  blocks are presumed decompressed next to each other\n *  up to the moment there is not enough remaining space for next block (remainingSize < maxBlockSize),\n *  at which stage it resumes from beginning of ring buffer.\n *  When setting such a ring buffer for streaming decompression,\n *  provides the minimum size of this ring buffer\n *  to be compatible with any source respecting maxBlockSize condition.\n * @return : minimum ring buffer size,\n *           or 0 if there is an error (invalid maxBlockSize).\n */\nLZ4LIB_API int LZ4_decoderRingBufferSize(int maxBlockSize);\n#define LZ4_DECODER_RING_BUFFER_SIZE(maxBlockSize) (65536 + 14 + (maxBlockSize))  /* for static allocation; maxBlockSize presumed valid */\n\n/*! LZ4_decompress_safe_continue() :\n *  This decoding function allows decompression of consecutive blocks in \"streaming\" mode.\n *  The difference with the usual independent blocks is that\n *  new blocks are allowed to find references into former blocks.\n *  A block is an unsplittable entity, and must be presented entirely to the decompression function.\n *  LZ4_decompress_safe_continue() only accepts one block at a time.\n *  It's modeled after `LZ4_decompress_safe()` and behaves similarly.\n *\n * @LZ4_streamDecode : decompression state, tracking the position in memory of past data\n * @compressedSize : exact complete size of one compressed block.\n * @dstCapacity : size of destination buffer (which must be already allocated),\n *                must be an upper bound of decompressed size.\n * @return : number of bytes decompressed into destination buffer (necessarily <= dstCapacity)\n *           If destination buffer is not large enough, decoding will stop and output an error code (negative value).\n *           If the source stream is detected malformed, the function will stop decoding and return a negative result.\n *\n *  The last 64KB of previously decoded data *must* remain available and unmodified\n *  at the memory position where they were previously decoded.\n *  If less than 64KB of data has been decoded, all the data must be present.\n *\n *  Special : if decompression side sets a ring buffer, it must respect one of the following conditions :\n *  - Decompression buffer size is _at least_ LZ4_decoderRingBufferSize(maxBlockSize).\n *    maxBlockSize is the maximum size of any single block. It can have any value > 16 bytes.\n *    In which case, encoding and decoding buffers do not need to be synchronized.\n *    Actually, data can be produced by any source compliant with LZ4 format specification, and respecting maxBlockSize.\n *  - Synchronized mode :\n *    Decompression buffer size is _exactly_ the same as compression buffer size,\n *    and follows exactly same update rule (block boundaries at same positions),\n *    and decoding function is provided with exact decompressed size of each block (exception for last block of the stream),\n *    _then_ decoding & encoding ring buffer can have any size, including small ones ( < 64 KB).\n *  - Decompression buffer is larger than encoding buffer, by a minimum of maxBlockSize more bytes.\n *    In which case, encoding and decoding buffers do not need to be synchronized,\n *    and encoding ring buffer can have any size, including small ones ( < 64 KB).\n *\n *  Whenever these conditions are not possible,\n *  save the last 64KB of decoded data into a safe buffer where it can't be modified during decompression,\n *  then indicate where this data is saved using LZ4_setStreamDecode(), before decompressing next block.\n*/\nLZ4LIB_API int\nLZ4_decompress_safe_continue (LZ4_streamDecode_t* LZ4_streamDecode,\n                        const char* src, char* dst,\n                        int srcSize, int dstCapacity);\n\n\n/*! LZ4_decompress_safe_usingDict() :\n *  Works the same as\n *  a combination of LZ4_setStreamDecode() followed by LZ4_decompress_safe_continue()\n *  However, it's stateless: it doesn't need any LZ4_streamDecode_t state.\n *  Dictionary is presumed stable : it must remain accessible and unmodified during decompression.\n *  Performance tip : Decompression speed can be substantially increased\n *                    when dst == dictStart + dictSize.\n */\nLZ4LIB_API int\nLZ4_decompress_safe_usingDict(const char* src, char* dst,\n                              int srcSize, int dstCapacity,\n                              const char* dictStart, int dictSize);\n\n/*! LZ4_decompress_safe_partial_usingDict() :\n *  Behaves the same as LZ4_decompress_safe_partial()\n *  with the added ability to specify a memory segment for past data.\n *  Performance tip : Decompression speed can be substantially increased\n *                    when dst == dictStart + dictSize.\n */\nLZ4LIB_API int\nLZ4_decompress_safe_partial_usingDict(const char* src, char* dst,\n                                      int compressedSize,\n                                      int targetOutputSize, int maxOutputSize,\n                                      const char* dictStart, int dictSize);\n\n#endif /* LZ4_H_2983827168210 */\n\n\n/*^*************************************\n * !!!!!!   STATIC LINKING ONLY   !!!!!!\n ***************************************/\n\n/*-****************************************************************************\n * Experimental section\n *\n * Symbols declared in this section must be considered unstable. Their\n * signatures or semantics may change, or they may be removed altogether in the\n * future. They are therefore only safe to depend on when the caller is\n * statically linked against the library.\n *\n * To protect against unsafe usage, not only are the declarations guarded,\n * the definitions are hidden by default\n * when building LZ4 as a shared/dynamic library.\n *\n * In order to access these declarations,\n * define LZ4_STATIC_LINKING_ONLY in your application\n * before including LZ4's headers.\n *\n * In order to make their implementations accessible dynamically, you must\n * define LZ4_PUBLISH_STATIC_FUNCTIONS when building the LZ4 library.\n ******************************************************************************/\n\n#ifdef LZ4_STATIC_LINKING_ONLY\n\n#ifndef LZ4_STATIC_3504398509\n#define LZ4_STATIC_3504398509\n\n#ifdef LZ4_PUBLISH_STATIC_FUNCTIONS\n#define LZ4LIB_STATIC_API LZ4LIB_API\n#else\n#define LZ4LIB_STATIC_API\n#endif\n\n\n/*! LZ4_compress_fast_extState_fastReset() :\n *  A variant of LZ4_compress_fast_extState().\n *\n *  Using this variant avoids an expensive initialization step.\n *  It is only safe to call if the state buffer is known to be correctly initialized already\n *  (see above comment on LZ4_resetStream_fast() for a definition of \"correctly initialized\").\n *  From a high level, the difference is that\n *  this function initializes the provided state with a call to something like LZ4_resetStream_fast()\n *  while LZ4_compress_fast_extState() starts with a call to LZ4_resetStream().\n */\nLZ4LIB_STATIC_API int LZ4_compress_fast_extState_fastReset (void* state, const char* src, char* dst, int srcSize, int dstCapacity, int acceleration);\n\n/*! LZ4_attach_dictionary() :\n *  This is an experimental API that allows\n *  efficient use of a static dictionary many times.\n *\n *  Rather than re-loading the dictionary buffer into a working context before\n *  each compression, or copying a pre-loaded dictionary's LZ4_stream_t into a\n *  working LZ4_stream_t, this function introduces a no-copy setup mechanism,\n *  in which the working stream references the dictionary stream in-place.\n *\n *  Several assumptions are made about the state of the dictionary stream.\n *  Currently, only streams which have been prepared by LZ4_loadDict() should\n *  be expected to work.\n *\n *  Alternatively, the provided dictionaryStream may be NULL,\n *  in which case any existing dictionary stream is unset.\n *\n *  If a dictionary is provided, it replaces any pre-existing stream history.\n *  The dictionary contents are the only history that can be referenced and\n *  logically immediately precede the data compressed in the first subsequent\n *  compression call.\n *\n *  The dictionary will only remain attached to the working stream through the\n *  first compression call, at the end of which it is cleared. The dictionary\n *  stream (and source buffer) must remain in-place / accessible / unchanged\n *  through the completion of the first compression call on the stream.\n */\nLZ4LIB_STATIC_API void\nLZ4_attach_dictionary(LZ4_stream_t* workingStream,\n                const LZ4_stream_t* dictionaryStream);\n\n\n/*! In-place compression and decompression\n *\n * It's possible to have input and output sharing the same buffer,\n * for highly constrained memory environments.\n * In both cases, it requires input to lay at the end of the buffer,\n * and decompression to start at beginning of the buffer.\n * Buffer size must feature some margin, hence be larger than final size.\n *\n * |<------------------------buffer--------------------------------->|\n *                             |<-----------compressed data--------->|\n * |<-----------decompressed size------------------>|\n *                                                  |<----margin---->|\n *\n * This technique is more useful for decompression,\n * since decompressed size is typically larger,\n * and margin is short.\n *\n * In-place decompression will work inside any buffer\n * which size is >= LZ4_DECOMPRESS_INPLACE_BUFFER_SIZE(decompressedSize).\n * This presumes that decompressedSize > compressedSize.\n * Otherwise, it means compression actually expanded data,\n * and it would be more efficient to store such data with a flag indicating it's not compressed.\n * This can happen when data is not compressible (already compressed, or encrypted).\n *\n * For in-place compression, margin is larger, as it must be able to cope with both\n * history preservation, requiring input data to remain unmodified up to LZ4_DISTANCE_MAX,\n * and data expansion, which can happen when input is not compressible.\n * As a consequence, buffer size requirements are much higher,\n * and memory savings offered by in-place compression are more limited.\n *\n * There are ways to limit this cost for compression :\n * - Reduce history size, by modifying LZ4_DISTANCE_MAX.\n *   Note that it is a compile-time constant, so all compressions will apply this limit.\n *   Lower values will reduce compression ratio, except when input_size < LZ4_DISTANCE_MAX,\n *   so it's a reasonable trick when inputs are known to be small.\n * - Require the compressor to deliver a \"maximum compressed size\".\n *   This is the `dstCapacity` parameter in `LZ4_compress*()`.\n *   When this size is < LZ4_COMPRESSBOUND(inputSize), then compression can fail,\n *   in which case, the return code will be 0 (zero).\n *   The caller must be ready for these cases to happen,\n *   and typically design a backup scheme to send data uncompressed.\n * The combination of both techniques can significantly reduce\n * the amount of margin required for in-place compression.\n *\n * In-place compression can work in any buffer\n * which size is >= (maxCompressedSize)\n * with maxCompressedSize == LZ4_COMPRESSBOUND(srcSize) for guaranteed compression success.\n * LZ4_COMPRESS_INPLACE_BUFFER_SIZE() depends on both maxCompressedSize and LZ4_DISTANCE_MAX,\n * so it's possible to reduce memory requirements by playing with them.\n */\n\n#define LZ4_DECOMPRESS_INPLACE_MARGIN(compressedSize)          (((compressedSize) >> 8) + 32)\n#define LZ4_DECOMPRESS_INPLACE_BUFFER_SIZE(decompressedSize)   ((decompressedSize) + LZ4_DECOMPRESS_INPLACE_MARGIN(decompressedSize))  /**< note: presumes that compressedSize < decompressedSize. note2: margin is overestimated a bit, since it could use compressedSize instead */\n\n#ifndef LZ4_DISTANCE_MAX   /* history window size; can be user-defined at compile time */\n#  define LZ4_DISTANCE_MAX 65535   /* set to maximum value by default */\n#endif\n\n#define LZ4_COMPRESS_INPLACE_MARGIN                           (LZ4_DISTANCE_MAX + 32)   /* LZ4_DISTANCE_MAX can be safely replaced by srcSize when it's smaller */\n#define LZ4_COMPRESS_INPLACE_BUFFER_SIZE(maxCompressedSize)   ((maxCompressedSize) + LZ4_COMPRESS_INPLACE_MARGIN)  /**< maxCompressedSize is generally LZ4_COMPRESSBOUND(inputSize), but can be set to any lower value, with the risk that compression can fail (return code 0(zero)) */\n\n#endif   /* LZ4_STATIC_3504398509 */\n#endif   /* LZ4_STATIC_LINKING_ONLY */\n\n\n\n#ifndef LZ4_H_98237428734687\n#define LZ4_H_98237428734687\n\n/*-************************************************************\n *  Private Definitions\n **************************************************************\n * Do not use these definitions directly.\n * They are only exposed to allow static allocation of `LZ4_stream_t` and `LZ4_streamDecode_t`.\n * Accessing members will expose user code to API and/or ABI break in future versions of the library.\n **************************************************************/\n#define LZ4_HASHLOG   (LZ4_MEMORY_USAGE-2)\n#define LZ4_HASHTABLESIZE (1 << LZ4_MEMORY_USAGE)\n#define LZ4_HASH_SIZE_U32 (1 << LZ4_HASHLOG)       /* required as macro for static allocation */\n\n#if defined(__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */)\n# include <stdint.h>\n  typedef  int8_t  LZ4_i8;\n  typedef uint8_t  LZ4_byte;\n  typedef uint16_t LZ4_u16;\n  typedef uint32_t LZ4_u32;\n#else\n  typedef   signed char  LZ4_i8;\n  typedef unsigned char  LZ4_byte;\n  typedef unsigned short LZ4_u16;\n  typedef unsigned int   LZ4_u32;\n#endif\n\n/*! LZ4_stream_t :\n *  Never ever use below internal definitions directly !\n *  These definitions are not API/ABI safe, and may change in future versions.\n *  If you need static allocation, declare or allocate an LZ4_stream_t object.\n**/\n\ntypedef struct LZ4_stream_t_internal LZ4_stream_t_internal;\nstruct LZ4_stream_t_internal {\n    LZ4_u32 hashTable[LZ4_HASH_SIZE_U32];\n    const LZ4_byte* dictionary;\n    const LZ4_stream_t_internal* dictCtx;\n    LZ4_u32 currentOffset;\n    LZ4_u32 tableType;\n    LZ4_u32 dictSize;\n    /* Implicit padding to ensure structure is aligned */\n};\n\n#define LZ4_STREAM_MINSIZE  ((1UL << LZ4_MEMORY_USAGE) + 32)  /* static size, for inter-version compatibility */\nunion LZ4_stream_u {\n    char minStateSize[LZ4_STREAM_MINSIZE];\n    LZ4_stream_t_internal internal_donotuse;\n}; /* previously typedef'd to LZ4_stream_t */\n\n\n/*! LZ4_initStream() : v1.9.0+\n *  An LZ4_stream_t structure must be initialized at least once.\n *  This is automatically done when invoking LZ4_createStream(),\n *  but it's not when the structure is simply declared on stack (for example).\n *\n *  Use LZ4_initStream() to properly initialize a newly declared LZ4_stream_t.\n *  It can also initialize any arbitrary buffer of sufficient size,\n *  and will @return a pointer of proper type upon initialization.\n *\n *  Note : initialization fails if size and alignment conditions are not respected.\n *         In which case, the function will @return NULL.\n *  Note2: An LZ4_stream_t structure guarantees correct alignment and size.\n *  Note3: Before v1.9.0, use LZ4_resetStream() instead\n**/\nLZ4LIB_API LZ4_stream_t* LZ4_initStream (void* buffer, size_t size);\n\n\n/*! LZ4_streamDecode_t :\n *  Never ever use below internal definitions directly !\n *  These definitions are not API/ABI safe, and may change in future versions.\n *  If you need static allocation, declare or allocate an LZ4_streamDecode_t object.\n**/\ntypedef struct {\n    const LZ4_byte* externalDict;\n    const LZ4_byte* prefixEnd;\n    size_t extDictSize;\n    size_t prefixSize;\n} LZ4_streamDecode_t_internal;\n\n#define LZ4_STREAMDECODE_MINSIZE 32\nunion LZ4_streamDecode_u {\n    char minStateSize[LZ4_STREAMDECODE_MINSIZE];\n    LZ4_streamDecode_t_internal internal_donotuse;\n} ;   /* previously typedef'd to LZ4_streamDecode_t */\n\n\n\n/*-************************************\n*  Obsolete Functions\n**************************************/\n\n/*! Deprecation warnings\n *\n *  Deprecated functions make the compiler generate a warning when invoked.\n *  This is meant to invite users to update their source code.\n *  Should deprecation warnings be a problem, it is generally possible to disable them,\n *  typically with -Wno-deprecated-declarations for gcc\n *  or _CRT_SECURE_NO_WARNINGS in Visual.\n *\n *  Another method is to define LZ4_DISABLE_DEPRECATE_WARNINGS\n *  before including the header file.\n */\n#ifdef LZ4_DISABLE_DEPRECATE_WARNINGS\n#  define LZ4_DEPRECATED(message)   /* disable deprecation warnings */\n#else\n#  if defined (__cplusplus) && (__cplusplus >= 201402) /* C++14 or greater */\n#    define LZ4_DEPRECATED(message) [[deprecated(message)]]\n#  elif defined(_MSC_VER)\n#    define LZ4_DEPRECATED(message) __declspec(deprecated(message))\n#  elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ * 10 + __GNUC_MINOR__ >= 45))\n#    define LZ4_DEPRECATED(message) __attribute__((deprecated(message)))\n#  elif defined(__GNUC__) && (__GNUC__ * 10 + __GNUC_MINOR__ >= 31)\n#    define LZ4_DEPRECATED(message) __attribute__((deprecated))\n#  else\n#    pragma message(\"WARNING: LZ4_DEPRECATED needs custom implementation for this compiler\")\n#    define LZ4_DEPRECATED(message)   /* disabled */\n#  endif\n#endif /* LZ4_DISABLE_DEPRECATE_WARNINGS */\n\n/*! Obsolete compression functions (since v1.7.3) */\nLZ4_DEPRECATED(\"use LZ4_compress_default() instead\")       LZ4LIB_API int LZ4_compress               (const char* src, char* dest, int srcSize);\nLZ4_DEPRECATED(\"use LZ4_compress_default() instead\")       LZ4LIB_API int LZ4_compress_limitedOutput (const char* src, char* dest, int srcSize, int maxOutputSize);\nLZ4_DEPRECATED(\"use LZ4_compress_fast_extState() instead\") LZ4LIB_API int LZ4_compress_withState               (void* state, const char* source, char* dest, int inputSize);\nLZ4_DEPRECATED(\"use LZ4_compress_fast_extState() instead\") LZ4LIB_API int LZ4_compress_limitedOutput_withState (void* state, const char* source, char* dest, int inputSize, int maxOutputSize);\nLZ4_DEPRECATED(\"use LZ4_compress_fast_continue() instead\") LZ4LIB_API int LZ4_compress_continue                (LZ4_stream_t* LZ4_streamPtr, const char* source, char* dest, int inputSize);\nLZ4_DEPRECATED(\"use LZ4_compress_fast_continue() instead\") LZ4LIB_API int LZ4_compress_limitedOutput_continue  (LZ4_stream_t* LZ4_streamPtr, const char* source, char* dest, int inputSize, int maxOutputSize);\n\n/*! Obsolete decompression functions (since v1.8.0) */\nLZ4_DEPRECATED(\"use LZ4_decompress_fast() instead\") LZ4LIB_API int LZ4_uncompress (const char* source, char* dest, int outputSize);\nLZ4_DEPRECATED(\"use LZ4_decompress_safe() instead\") LZ4LIB_API int LZ4_uncompress_unknownOutputSize (const char* source, char* dest, int isize, int maxOutputSize);\n\n/* Obsolete streaming functions (since v1.7.0)\n * degraded functionality; do not use!\n *\n * In order to perform streaming compression, these functions depended on data\n * that is no longer tracked in the state. They have been preserved as well as\n * possible: using them will still produce a correct output. However, they don't\n * actually retain any history between compression calls. The compression ratio\n * achieved will therefore be no better than compressing each chunk\n * independently.\n */\nLZ4_DEPRECATED(\"Use LZ4_createStream() instead\") LZ4LIB_API void* LZ4_create (char* inputBuffer);\nLZ4_DEPRECATED(\"Use LZ4_createStream() instead\") LZ4LIB_API int   LZ4_sizeofStreamState(void);\nLZ4_DEPRECATED(\"Use LZ4_resetStream() instead\")  LZ4LIB_API int   LZ4_resetStreamState(void* state, char* inputBuffer);\nLZ4_DEPRECATED(\"Use LZ4_saveDict() instead\")     LZ4LIB_API char* LZ4_slideInputBuffer (void* state);\n\n/*! Obsolete streaming decoding functions (since v1.7.0) */\nLZ4_DEPRECATED(\"use LZ4_decompress_safe_usingDict() instead\") LZ4LIB_API int LZ4_decompress_safe_withPrefix64k (const char* src, char* dst, int compressedSize, int maxDstSize);\nLZ4_DEPRECATED(\"use LZ4_decompress_fast_usingDict() instead\") LZ4LIB_API int LZ4_decompress_fast_withPrefix64k (const char* src, char* dst, int originalSize);\n\n/*! Obsolete LZ4_decompress_fast variants (since v1.9.0) :\n *  These functions used to be faster than LZ4_decompress_safe(),\n *  but this is no longer the case. They are now slower.\n *  This is because LZ4_decompress_fast() doesn't know the input size,\n *  and therefore must progress more cautiously into the input buffer to not read beyond the end of block.\n *  On top of that `LZ4_decompress_fast()` is not protected vs malformed or malicious inputs, making it a security liability.\n *  As a consequence, LZ4_decompress_fast() is strongly discouraged, and deprecated.\n *\n *  The last remaining LZ4_decompress_fast() specificity is that\n *  it can decompress a block without knowing its compressed size.\n *  Such functionality can be achieved in a more secure manner\n *  by employing LZ4_decompress_safe_partial().\n *\n *  Parameters:\n *  originalSize : is the uncompressed size to regenerate.\n *                 `dst` must be already allocated, its size must be >= 'originalSize' bytes.\n * @return : number of bytes read from source buffer (== compressed size).\n *           The function expects to finish at block's end exactly.\n *           If the source stream is detected malformed, the function stops decoding and returns a negative result.\n *  note : LZ4_decompress_fast*() requires originalSize. Thanks to this information, it never writes past the output buffer.\n *         However, since it doesn't know its 'src' size, it may read an unknown amount of input, past input buffer bounds.\n *         Also, since match offsets are not validated, match reads from 'src' may underflow too.\n *         These issues never happen if input (compressed) data is correct.\n *         But they may happen if input data is invalid (error or intentional tampering).\n *         As a consequence, use these functions in trusted environments with trusted data **only**.\n */\nLZ4_DEPRECATED(\"This function is deprecated and unsafe. Consider using LZ4_decompress_safe() instead\")\nLZ4LIB_API int LZ4_decompress_fast (const char* src, char* dst, int originalSize);\nLZ4_DEPRECATED(\"This function is deprecated and unsafe. Consider using LZ4_decompress_safe_continue() instead\")\nLZ4LIB_API int LZ4_decompress_fast_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* src, char* dst, int originalSize);\nLZ4_DEPRECATED(\"This function is deprecated and unsafe. Consider using LZ4_decompress_safe_usingDict() instead\")\nLZ4LIB_API int LZ4_decompress_fast_usingDict (const char* src, char* dst, int originalSize, const char* dictStart, int dictSize);\n\n/*! LZ4_resetStream() :\n *  An LZ4_stream_t structure must be initialized at least once.\n *  This is done with LZ4_initStream(), or LZ4_resetStream().\n *  Consider switching to LZ4_initStream(),\n *  invoking LZ4_resetStream() will trigger deprecation warnings in the future.\n */\nLZ4LIB_API void LZ4_resetStream (LZ4_stream_t* streamPtr);\n\n\n#endif /* LZ4_H_98237428734687 */\n\n\n#if defined (__cplusplus)\n}\n#endif\n"
  },
  {
    "path": "jni/msp/msp.c",
    "content": "#include <stdlib.h>\n#include <string.h>\n#include <stdio.h>\n#include \"msp.h\"\n\nuint16_t msp_data_from_msg(uint8_t message_buffer[], msp_msg_t *msg) {\n    // return size\n    construct_msp_command(message_buffer, msg->cmd, msg->payload, msg->size, msg->direction);\n    return msg->size + 6;\n}\n\nmsp_error_e construct_msp_command(uint8_t message_buffer[], uint8_t command, uint8_t payload[], uint8_t size, msp_direction_e direction) {\n    uint8_t checksum;\n    message_buffer[0] = '$'; // Header\n    message_buffer[1] = 'M'; // MSP V1\n    if (direction == MSP_OUTBOUND) {\n        message_buffer[2] = '<';\n    } else {\n        message_buffer[2] = '>';\n    }\n    message_buffer[3] = size; // Payload Size\n    checksum = size;\n    message_buffer[4] = command; // Command\n    checksum ^= command;\n    for(uint8_t i = 0; i < size; i++) {\n        message_buffer[5 + i] = payload[i];\n        checksum ^= message_buffer[5 + i];\n    }\n    message_buffer[5 + size] = checksum;\n    return 0;\n}\n\nmsp_error_e msp_process_data(msp_state_t *msp_state, uint8_t dat)\n{\n    switch (msp_state->state)\n    {\n        default:\n        case MSP_IDLE: // look for begin\n            if (dat == '$')\n            {\n                msp_state->state = MSP_VERSION;\n            }\n            else\n            {\n                return MSP_ERR_HDR;\n            }\n            break;\n        case MSP_VERSION: // Look for 'M' (MSP V1, we do not support V2 at this time)\n            if (dat == 'M')\n            {\n                msp_state->state = MSP_DIRECTION;\n            }\n            else\n            { // Got garbage instead, try again\n                msp_state->state = MSP_IDLE;\n                return MSP_ERR_HDR;\n            }\n            break;\n        case MSP_DIRECTION: // < for command, > for reply\n            msp_state->state = MSP_SIZE;\n            switch (dat)\n            {\n            case '<':\n                msp_state->message.direction = MSP_OUTBOUND;\n                break;\n            case '>':\n                msp_state->message.direction = MSP_INBOUND;\n                break;\n            default: // garbage, try again\n                msp_state->state = MSP_IDLE;\n                return MSP_ERR_HDR;\n                break;\n            }\n            break;\n        case MSP_SIZE: // next up is supposed to be size\n            msp_state->message.checksum = dat;\n            msp_state->message.size = dat;\n            msp_state->state = MSP_CMD;\n            if (msp_state->message.size > 256)\n            { // bogus message, too big. this can't actually happen but good to check\n                msp_state->state = MSP_IDLE;\n                return MSP_ERR_LEN;\n                break;\n            }\n            break;\n        case MSP_CMD: // followed by command\n            msp_state->message.cmd = dat;\n            msp_state->message.checksum ^= dat;\n            msp_state->buf_ptr = 0;\n            if (msp_state->message.size > 0)\n            {\n                msp_state->state = MSP_PAYLOAD;\n            }\n            else\n            {\n                msp_state->state = MSP_CHECKSUM;\n            }\n            break;\n        case MSP_PAYLOAD: // if we had a payload, keep going\n            msp_state->message.payload[msp_state->buf_ptr] = dat;\n            msp_state->message.checksum ^= dat;\n            msp_state->buf_ptr++;\n            if (msp_state->buf_ptr == msp_state->message.size)\n            {\n                msp_state->buf_ptr = 0;\n                msp_state->state = MSP_CHECKSUM;\n            }\n            break;\n        case MSP_CHECKSUM:\n            if (msp_state->message.checksum == dat)\n            {\n                if (msp_state->cb != 0)\n                {\n                    msp_state->cb(&msp_state->message);\n                }\n                memset(&msp_state->message, 0, sizeof(msp_msg_t));\n                msp_state->state = MSP_IDLE;\n                break;            \n            }\n            else\n            {\n                msp_state->state = MSP_IDLE;\n                return MSP_ERR_CKS;\n            }\n            break;\n    }\n    return MSP_ERR_NONE;\n}"
  },
  {
    "path": "jni/msp/msp.h",
    "content": "#pragma once\n#include <stdint.h>\n\n#define MSP_CMD_API_VERSION 1\n#define MSP_CMD_FC_VARIANT 2\n#define MSP_CMD_FC_VERSION 3\n#define MSP_CMD_NAME 10\n#define MSP_CMD_FILTER_CONFIG 92\n#define MSP_CMD_PID_ADVANCED 94\n#define MSP_CMD_STATUS 101\n#define MSP_CMD_RC 105\n#define MSP_CMD_ANALOG 110\n#define MSP_CMD_RC_TUNING 111\n#define MSP_CMD_PID 112\n#define MSP_CMD_BATTERY_STATE 130\n#define MSP_CMD_STATUS_EX 150\n#define MSP_CMD_DISPLAYPORT 182\n#define MSP_CMD_SET_OSD_CANVAS 188\n\ntypedef enum {\n    MSP_ERR_NONE,\n    MSP_ERR_HDR,\n    MSP_ERR_LEN,\n    MSP_ERR_CKS\n} msp_error_e;\n\ntypedef enum {\n    MSP_IDLE,\n    MSP_VERSION,\n    MSP_DIRECTION,\n    MSP_SIZE,\n    MSP_CMD,\n    MSP_PAYLOAD,\n    MSP_CHECKSUM,\n} msp_state_machine_e;\n\ntypedef enum {\n    MSP_INBOUND,\n    MSP_OUTBOUND\n} msp_direction_e;\n\ntypedef struct msp_msg_s {\n    uint8_t checksum;\n    uint8_t cmd;\n    uint8_t size;\n    msp_direction_e direction;\n    uint8_t payload[256];\n} msp_msg_t;\n\ntypedef void (*msp_msg_callback)(msp_msg_t *);\n\ntypedef struct msp_state_s {\n    msp_msg_callback cb;\n    msp_state_machine_e state;\n    uint8_t buf_ptr;\n    msp_msg_t message;\n} msp_state_t;\n\nuint16_t msp_data_from_msg(uint8_t message_buffer[], msp_msg_t *msg);\nmsp_error_e construct_msp_command(uint8_t message_buffer[], uint8_t command, uint8_t payload[], uint8_t size, msp_direction_e direction);\nmsp_error_e msp_process_data(msp_state_t *msp_state, uint8_t dat);"
  },
  {
    "path": "jni/msp/msp_displayport.c",
    "content": "#include \"msp.h\"\n#include \"msp_displayport.h\"\n\nstatic void process_draw_string(displayport_vtable_t *display_driver, uint8_t *payload) {\n    if(!display_driver || !display_driver->draw_character) return;\n    uint8_t row = payload[0];\n    uint8_t col = payload[1];\n    uint8_t attrs = payload[2]; // INAV and Betaflight use this to specify a higher page number. \n    uint8_t str_len;\n    for(str_len = 1; str_len < 255; str_len++) {\n        if(payload[2 + str_len] == '\\0') {\n            break;\n        }\n    }\n    for(uint8_t idx = 0; idx < (str_len - 1); idx++) {\n        uint16_t character = payload[3 + idx];\n        if(attrs & 0x3) {\n            // shift over by the page number if they were specified\n            character |= ((attrs & 0x3) * 0x100);\n        }\n        display_driver->draw_character(col, row, character);\n        col++;\n    }\n}\n\nstatic void process_clear_screen(displayport_vtable_t *display_driver) {\n    if(!display_driver || !display_driver->clear_screen) return;\n    display_driver->clear_screen();\n}\n\nstatic void process_draw_complete(displayport_vtable_t *display_driver) {\n    if(!display_driver || !display_driver->draw_complete) return;\n    display_driver->draw_complete();\n}\n\nstatic void process_set_options(displayport_vtable_t *display_driver, uint8_t *payload) {\n    if(!display_driver || !display_driver->set_options) return;\n    uint8_t font = payload[0];\n    msp_hd_options_e is_hd = payload[1];\n    display_driver->set_options(font, is_hd);\n}\n\nstatic void process_open(displayport_vtable_t *display_driver) {\n\n}\n\nstatic void process_close(displayport_vtable_t *display_driver) {\n    process_clear_screen(display_driver);\n}\n\nint displayport_process_message(displayport_vtable_t *display_driver, msp_msg_t *msg) {\n    if (msg->direction != MSP_INBOUND) {\n        return 1;\n    }\n    if (msg->cmd != MSP_CMD_DISPLAYPORT) {\n        return 1;\n    }\n    msp_displayport_cmd_e sub_cmd = msg->payload[0];\n    switch(sub_cmd) {\n        case MSP_DISPLAYPORT_KEEPALIVE: // 0 -> Open/Keep-Alive DisplayPort\n            process_open(display_driver);\n            break;\n        case MSP_DISPLAYPORT_CLOSE: // 1 -> Close DisplayPort\n            process_close(display_driver);\n            break;\n        case MSP_DISPLAYPORT_CLEAR: // 2 -> Clear Screen\n            process_clear_screen(display_driver);\n            break;\n        case MSP_DISPLAYPORT_DRAW_STRING: // 3 -> Draw String\n            process_draw_string(display_driver, &msg->payload[1]);\n            break;\n        case MSP_DISPLAYPORT_DRAW_SCREEN: // 4 -> Draw Screen\n            process_draw_complete(display_driver);\n            break;\n        case MSP_DISPLAYPORT_SET_OPTIONS: // 5 -> Set Options (HDZero/iNav)\n            process_set_options(display_driver, &msg->payload[1]);\n            break;\n        default:\n            break;\n    }\n    return 0;\n}\n\n"
  },
  {
    "path": "jni/msp/msp_displayport.h",
    "content": "#pragma once\n#include <stdint.h>\n#include \"msp.h\"\n\ntypedef enum {\n    MSP_DISPLAYPORT_KEEPALIVE,\n    MSP_DISPLAYPORT_CLOSE,\n    MSP_DISPLAYPORT_CLEAR,\n    MSP_DISPLAYPORT_DRAW_STRING,\n    MSP_DISPLAYPORT_DRAW_SCREEN,\n    MSP_DISPLAYPORT_SET_OPTIONS,\n    MSP_DISPLAYPORT_DRAW_SYSTEM\n} msp_displayport_cmd_e;\n\ntypedef enum {\n    MSP_SD_OPTION_30_16,\n    MSP_HD_OPTION_50_18,\n    MSP_HD_OPTION_30_16,\n    MSP_HD_OPTION_60_22\n} msp_hd_options_e;\n\ntypedef void (*draw_character_func)(uint32_t x, uint32_t y, uint16_t c);\ntypedef void (*set_options_func)(uint8_t font, msp_hd_options_e is_hd);\ntypedef void (*clear_screen_func)();\ntypedef void (*draw_complete_func)();\n\ntypedef struct displayport_vtable_s {\n    draw_character_func draw_character;\n    clear_screen_func clear_screen;\n    draw_complete_func draw_complete;\n    set_options_func set_options;\n} displayport_vtable_t;\n\nint displayport_process_message(displayport_vtable_t *display_driver, msp_msg_t *msg);"
  },
  {
    "path": "jni/msp_displayport_mux.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <unistd.h>\n#include <string.h>\n#include <sys/poll.h>\n#include <time.h>\n\n#include \"hw/dji_radio_shm.h\"\n#include \"json/osd_config.h\"\n#include \"lz4/lz4.h\"\n#include \"net/data_protocol.h\"\n#include \"net/network.h\"\n#include \"net/serial.h\"\n#include \"msp/msp.h\"\n#include \"msp/msp_displayport.h\"\n#include \"util/debug.h\"\n#include \"util/time_util.h\"\n#include \"util/fs_util.h\"\n\n#define CPU_TEMP_PATH \"/sys/devices/platform/soc/f0a00000.apb/f0a71000.omc/temp1\"\n#define AU_VOLTAGE_PATH \"/sys/devices/platform/soc/f0a00000.apb/f0a71000.omc/voltage4\"\n\n#define FAST_SERIAL_KEY \"fast_serial\"\n#define CACHE_SERIAL_KEY \"cache_serial\"\n#define COMPRESS_KEY \"compress_osd\"\n#define UPDATE_RATE_KEY \"osd_update_rate_hz\"\n#define NO_BTFL_HD_KEY \"disable_betaflight_hd\"\n\n// The MSP_PORT is used to send MSP passthrough messages.\n// The DATA_PORT is used to send arbitrary data - for example, bitrate and temperature data.\n\n#define MSP_PORT 7654\n#define DATA_PORT 7655\n#define COMPRESSED_DATA_PORT 7656\n\n#define COMPRESSED_DATA_VERSION 1\n\nenum {\n    MAX_DISPLAY_X = 60,\n    MAX_DISPLAY_Y = 22\n};\n\n// The Betaflight MSP minor version in which MSP DisplayPort sizing is supported.\n#define MSP_DISPLAY_SIZE_VERSION 45\n\ntypedef struct msp_cache_entry_s {\n    struct timespec time;\n    msp_msg_t message;\n} msp_cache_entry_t;\n\nstatic msp_cache_entry_t *msp_message_cache[256]; // make a slot for all possible messages\n\nstatic uint8_t frame_buffer[8192]; // buffer a whole frame of MSP commands until we get a draw command\nstatic uint32_t fb_cursor = 0;\n\nstatic uint8_t message_buffer[256]; // only needs to be the maximum size of an MSP packet, we only care to fwd MSP\nstatic char current_fc_identifier[4];\n\n/* For compressed full-frame transmission */\nstatic uint16_t msp_character_map_buffer[MAX_DISPLAY_X][MAX_DISPLAY_Y];\nstatic uint16_t msp_character_map_draw[MAX_DISPLAY_X][MAX_DISPLAY_Y];\nstatic msp_hd_options_e msp_hd_option = 0;\nstatic displayport_vtable_t *display_driver = NULL;\nLZ4_stream_t *lz4_ref_ctx = NULL;\nuint8_t update_rate_hz = 2;\n\nint pty_fd;\nint serial_fd;\nint socket_fd;\nint compressed_fd;\n\nstatic volatile sig_atomic_t quit = 0;\nstatic uint8_t serial_passthrough = 1;\nstatic uint8_t compress = 0;\nstatic uint8_t no_btfl_hd = 0;\n\nstatic void sig_handler(int _)\n{\n    quit = 1;\n}\n\nstatic uint8_t cache_msp_message(msp_msg_t *msp_message) {\n    // 0 -> cache overwritten\n    // 1 -> freshly cached\n    uint8_t retval = 0;\n    msp_cache_entry_t *cache_message = msp_message_cache[msp_message->cmd];\n    if (cache_message == NULL) {\n        DEBUG_PRINT(\"FC -> AU CACHE: no entry for msg %d, allocating\\n\", msp_message->cmd);\n        cache_message = calloc(1, sizeof(msp_cache_entry_t));\n        msp_message_cache[msp_message->cmd] = cache_message;\n        retval = 1;\n    }\n    DEBUG_PRINT (\"FC -> AU CACHE: refreshing %d\\n\", msp_message->cmd);\n    memcpy(&cache_message->message, msp_message, sizeof(msp_msg_t));\n    clock_gettime(CLOCK_MONOTONIC, &cache_message->time);\n    return retval;\n}\n\nstatic int16_t msp_msg_from_cache(uint8_t msg_buffer[], uint8_t cmd_id) {\n    // returns size of message or -1\n    msp_cache_entry_t *cache_message = msp_message_cache[cmd_id];\n    if (cache_message == NULL) {\n        // cache missed, return -1 to trigger a serial send\n        return -1;\n    } else {\n        // cache hit, let's see if it's too old\n        // messages we care to expire after second boundary\n        if(cmd_id == MSP_CMD_ANALOG\n        || cmd_id == MSP_CMD_STATUS\n        || cmd_id == MSP_CMD_BATTERY_STATE) {\n            struct timespec now;\n            clock_gettime(CLOCK_MONOTONIC, &now);\n            if(timespec_subtract_ns(&now, &cache_message->time) > NSEC_PER_SEC) {\n                // message is too old, invalidate cache and force a resend\n                DEBUG_PRINT(\"MSP cache EXPIRED %d\\n\", cmd_id);\n                free(cache_message);\n                msp_message_cache[cmd_id] = 0;\n                return -1;\n            }\n        }\n        // message existed and was not stale, send it back\n        return msp_data_from_msg(msg_buffer, &cache_message->message);\n    }\n}\n\nstatic void send_display_size(int serial_fd) {\n    uint8_t buffer[8];\n    uint8_t payload[2] = {MAX_DISPLAY_X, MAX_DISPLAY_Y};\n    construct_msp_command(buffer, MSP_CMD_SET_OSD_CANVAS, payload, 2, MSP_OUTBOUND);\n    write(serial_fd, &buffer, sizeof(buffer));\n}\n\nstatic void send_variant_request(int serial_fd) {\n    uint8_t buffer[6];\n    construct_msp_command(buffer, MSP_CMD_FC_VARIANT, NULL, 0, MSP_OUTBOUND);\n    write(serial_fd, &buffer, sizeof(buffer));\n}\n\nstatic void send_version_request(int serial_fd) {\n    uint8_t buffer[6];\n    construct_msp_command(buffer, MSP_CMD_API_VERSION, NULL, 0, MSP_OUTBOUND);\n    write(serial_fd, &buffer, sizeof(buffer));\n}\n\nstatic void copy_to_msp_frame_buffer(void *buffer, uint16_t size) {\n    memcpy(&frame_buffer[fb_cursor], buffer, size);\n    fb_cursor += size;\n}\n\nstatic void rx_msp_callback(msp_msg_t *msp_message)\n{\n    // Process a received MSP message from FC and decide whether to send it to the PTY (DJI) or UDP port (MSP-OSD on Goggles)\n    DEBUG_PRINT(\"FC->AU MSP msg %d with data len %d \\n\", msp_message->cmd, msp_message->size);\n    switch(msp_message->cmd) {\n        case MSP_CMD_DISPLAYPORT: {\n            if (compress) {\n                // This was an MSP DisplayPort message and we're in compressed mode, so pass it off to the DisplayPort handlers.\n                displayport_process_message(display_driver, msp_message);\n            } else {\n                // This was an MSP DisplayPort message and we're in legacy mode, so buffer it until we get a whole frame.\n                if(fb_cursor > sizeof(frame_buffer)) {\n                    printf(\"Exhausted frame buffer! Resetting...\\n\");\n                    fb_cursor = 0;\n                    return;\n                }\n                uint16_t size = msp_data_from_msg(message_buffer, msp_message);\n                copy_to_msp_frame_buffer(message_buffer, size);\n                if(msp_message->payload[0] == MSP_DISPLAYPORT_DRAW_SCREEN) {\n                    // Once we have a whole frame of data, send it to the goggles.\n                    write(socket_fd, frame_buffer, fb_cursor);\n                    DEBUG_PRINT(\"DRAW! wrote %d bytes\\n\", fb_cursor);\n                    fb_cursor = 0;\n                }\n            }\n            break;\n        }\n        case MSP_CMD_FC_VARIANT: {\n            // This is an FC Variant response, so we want to use it to set our FC variant.\n            DEBUG_PRINT(\"Got FC Variant response!\\n\");\n            if(strncmp(current_fc_identifier, msp_message->payload, 4) != 0) {\n                // FC variant changed or was updated. Update the current FC identifier and send an MSP version request.\n                memcpy(current_fc_identifier, msp_message->payload, 4);\n                send_version_request(serial_fd);\n            }\n            break;\n        }\n        case MSP_CMD_API_VERSION: {\n            // Got an MSP API version response. Compare the version if we have Betaflight in order to see if we should send the new display size message.\n            if(strncmp(current_fc_identifier, \"BTFL\", 4) == 0) {\n                uint8_t msp_minor_version = msp_message->payload[2];\n                DEBUG_PRINT(\"Got Betaflight minor MSP version %d\\n\", msp_minor_version);\n                if(msp_minor_version >= MSP_DISPLAY_SIZE_VERSION) {\n                    if(!no_btfl_hd) {\n                        if(!compress) {\n                            // If compression is disabled, we need to manually inject a canvas-change command into the command stream.\n                            uint8_t displayport_set_size[3] = {MSP_DISPLAYPORT_SET_OPTIONS, 0, MSP_HD_OPTION_60_22};\n                            construct_msp_command(message_buffer, MSP_CMD_DISPLAYPORT, displayport_set_size, 3, MSP_INBOUND);\n                            copy_to_msp_frame_buffer(message_buffer, 9);\n                            DEBUG_PRINT(\"Sent display size to goggles\\n\");\n\n                        }\n                        // Betaflight with HD support. Send our display size and set 60x22.\n                        send_display_size(serial_fd);\n                        msp_hd_option = MSP_HD_OPTION_60_22;\n                        DEBUG_PRINT(\"Sent display size to FC\\n\");\n                    }\n                }\n            }\n            break;\n        }\n        default: {\n            uint16_t size = msp_data_from_msg(message_buffer, msp_message);\n            if(serial_passthrough || cache_msp_message(msp_message)) {\n                // Either serial passthrough was on, or the cache was enabled but missed (a response was not available). \n                // Either way, this means we need to send the message through to DJI.\n                write(pty_fd, message_buffer, size);\n            }\n            break;\n        }\n    }\n}\n\nstatic void tx_msp_callback(msp_msg_t *msp_message)\n{\n    // We got a valid message from DJI asking for something. See if there's a response in the cache or not.\n    // We can only get here if serial passthrough is off and caching is on, so no need to check again.\n    DEBUG_PRINT(\"DJI->FC MSP msg %d with request len %d \\n\", msp_message->cmd, msp_message->size);\n    uint8_t send_buffer[256];\n    int16_t size;\n    if(0 < (size = msp_msg_from_cache(send_buffer, msp_message->cmd))) {\n        // cache hit, so write the cached message straight back to DJI\n        DEBUG_PRINT(\"DJI->FC MSP CACHE HIT msg %d with response len %d \\n\", msp_message->cmd, size);\n        for(int i = 0; i < size; i++) {\n            DEBUG_PRINT(\"%02X \", send_buffer[i]);\n        }\n        DEBUG_PRINT(\"\\n\");\n        write(pty_fd, send_buffer, size);\n    } else {\n        // cache miss, so write the DJI request to serial and wait for the FC to come back.\n        DEBUG_PRINT(\"DJI->FC MSP CACHE MISS msg %d\\n\",msp_message->cmd);\n        uint16_t size = msp_data_from_msg(message_buffer, msp_message);\n        write(serial_fd, message_buffer, size);\n    }\n}\n\nstatic void send_data_packet(int data_socket_fd, dji_shm_state_t *dji_shm) {\n    packet_data_t data;\n    memset(&data, 0, sizeof(data));\n    data.version_specifier = 0xFFFF;\n    data.tx_temperature = get_int_from_fs(CPU_TEMP_PATH);\n    data.tx_voltage = get_int_from_fs(AU_VOLTAGE_PATH);\n    memcpy(data.fc_variant, current_fc_identifier, sizeof(current_fc_identifier));\n    DEBUG_PRINT(\"got voltage %f V temp %d C variant %.4s\\n\", (float)(data.tx_voltage / 64.0f), data.tx_temperature, data.fc_variant);\n    write(data_socket_fd, &data, sizeof(data));\n}\n\n/* MSP DisplayPort handlers for compressed mode */\n\nstatic void msp_draw_character(uint32_t x, uint32_t y, uint16_t c) {\n    DEBUG_PRINT(\"drawing char %d at x %d y %d\\n\", c, x, y);\n    msp_character_map_buffer[x][y] = c;\n}\n\nstatic void msp_clear_screen() {\n    memset(msp_character_map_buffer, 0, sizeof(msp_character_map_buffer));\n}\n\nstatic void msp_draw_complete() {\n    memcpy(msp_character_map_draw, msp_character_map_buffer, sizeof(msp_character_map_buffer));\n}\n\nstatic void msp_set_options(uint8_t font_num, msp_hd_options_e is_hd) {\n   DEBUG_PRINT(\"Got options!\\n\");\n   msp_clear_screen();\n   msp_hd_option = is_hd;\n}\n\nstatic void send_compressed_screen(int compressed_fd) {\n    LZ4_stream_t current_stream_state;\n    uint8_t dest_buf[sizeof(compressed_data_header_t) + LZ4_COMPRESSBOUND(sizeof(msp_character_map_draw))];\n    void *dest = &dest_buf;\n    memcpy(&current_stream_state, lz4_ref_ctx, sizeof(LZ4_stream_t));\n    int size = LZ4_compress_fast_extState_fastReset(&current_stream_state, msp_character_map_draw, (dest + sizeof(compressed_data_header_t)), sizeof(msp_character_map_draw), LZ4_compressBound(sizeof(msp_character_map_draw)), 1);\n    compressed_data_header_t *dest_header = (compressed_data_header_t *)dest;\n    dest_header->hd_options =(uint16_t)msp_hd_option;\n    dest_header->version = COMPRESSED_DATA_VERSION;\n    write(compressed_fd, dest, size + sizeof(compressed_data_header_t));\n    DEBUG_PRINT(\"COMPRESSED: Sent %d bytes!\\n\", size);\n}\n\nint main(int argc, char *argv[]) {\n    memset(current_fc_identifier, 0, sizeof(current_fc_identifier));\n    memset(msp_character_map_buffer, 0, sizeof(msp_character_map_buffer));\n    memset(msp_character_map_draw, 0, sizeof(msp_character_map_draw));\n\n    int compression_dict_size = 0;\n    void *compression_dict = open_dict(COMPRESSED_DATA_VERSION, &compression_dict_size);\n\n    int opt;\n    uint8_t fast_serial = 0;\n    uint8_t msp_command_number = 0;\n    while((opt = getopt(argc, argv, \"fsp\")) != -1){\n        switch(opt){\n        case 'f':\n            fast_serial = 1;\n            break;\n        case 's':\n            serial_passthrough = 0;\n            break;\n        case '?':\n            printf(\"unknown option: %c\\n\", optopt);\n            break;\n        }\n    }\n\n    if((argc - optind) < 2) {\n        printf(\"usage: msp_displayport_mux [-f] [-s] ipaddr serial_port [pty_target]\\n-s : enable serial caching\\n-f : 230400 baud serial\\n\");\n        return 0;\n    }\n\n    if(get_boolean_config_value(FAST_SERIAL_KEY)) {\n        fast_serial = 1;\n    }\n\n    if(get_boolean_config_value(CACHE_SERIAL_KEY)) {\n        serial_passthrough = 0;\n    }\n\n    if(get_boolean_config_value(COMPRESS_KEY)) {\n        compress = 1;\n    }\n\n    if(get_boolean_config_value(NO_BTFL_HD_KEY)) {\n        no_btfl_hd = 1;\n    }\n\n    if(fast_serial == 1) {\n        printf(\"Configured to use 230400 baud rate. \\n\");\n    }\n\n    if(serial_passthrough == 0) {\n        printf(\"Configured to use serial caching. \\n\");\n    }\n\n    dji_shm_state_t dji_radio;\n    memset(&dji_radio, 0, sizeof(dji_radio));\n    open_dji_radio_shm(&dji_radio);\n\n    char *ip_address = argv[optind];\n    char *serial_port = argv[optind + 1];\n    signal(SIGINT, sig_handler);\n    struct pollfd poll_fds[2];\n    const char *pty_name_ptr;\n    msp_state_t *rx_msp_state = calloc(1, sizeof(msp_state_t));\n    msp_state_t *tx_msp_state = calloc(1, sizeof(msp_state_t));\n    rx_msp_state->cb = &rx_msp_callback;\n    tx_msp_state->cb = &tx_msp_callback;\n    serial_fd = open_serial_port(serial_port, fast_serial ? B230400 : B115200);\n    if (serial_fd <= 0) {\n        printf(\"Failed to open serial port!\\n\");\n        return 1;\n    }\n    pty_fd = open_pty(&pty_name_ptr);\n    printf(\"Allocated PTY %s\\n\", pty_name_ptr);\n    if ((argc - optind) > 2) {\n        unlink(argv[optind + 2]);\n        symlink(pty_name_ptr, argv[optind + 2]);   \n        printf(\"Relinked %s to %s\\n\", argv[optind + 2], pty_name_ptr); \n    }\n    socket_fd = connect_to_server(ip_address, MSP_PORT);\n    compressed_fd = connect_to_server(ip_address, COMPRESSED_DATA_PORT);\n    int data_fd = connect_to_server(ip_address, DATA_PORT);\n\n    if (compress) {\n        update_rate_hz = get_integer_config_value(UPDATE_RATE_KEY);\n        display_driver = calloc(1, sizeof(displayport_vtable_t));\n        display_driver->draw_character = &msp_draw_character;\n        display_driver->clear_screen = &msp_clear_screen;\n        display_driver->draw_complete = &msp_draw_complete;\n        display_driver->set_options = &msp_set_options;\n\n        lz4_ref_ctx = LZ4_createStream();\n        LZ4_loadDict(lz4_ref_ctx, compression_dict, compression_dict_size);\n    }\n\n    uint8_t serial_data[256];\n    ssize_t serial_data_size;\n    struct timespec now, last_data, last_frame;\n    clock_gettime(CLOCK_MONOTONIC, &now);\n    clock_gettime(CLOCK_MONOTONIC, &last_data);\n    clock_gettime(CLOCK_MONOTONIC, &last_frame);\n\n    while (!quit) {\n        poll_fds[0].fd = serial_fd;\n        poll_fds[1].fd = pty_fd;\n        poll_fds[0].events = POLLIN;\n        poll_fds[1].events = POLLIN;\n\n        poll(poll_fds, 2, ((MSEC_PER_SEC / update_rate_hz) / 2));\n        \n        // We got inbound serial data, process it as MSP data.\n        if (0 < (serial_data_size = read(serial_fd, serial_data, sizeof(serial_data)))) {\n            DEBUG_PRINT(\"RECEIVED data! length %d\\n\", serial_data_size);\n            for (ssize_t i = 0; i < serial_data_size; i++) {\n                msp_process_data(rx_msp_state, serial_data[i]);\n            }\n        }\n        // We got data from DJI (the pty), so see what to do next:\n        if(0 < (serial_data_size = read(pty_fd, serial_data, sizeof(serial_data)))) {\n            if (serial_passthrough) {\n                // If serial passthrough is enabled, send the message through verbatim.\n                DEBUG_PRINT(\"SEND data! length %d\\n\", serial_data_size);\n                for (ssize_t i= 0; i < serial_data_size; i++) {\n                    DEBUG_PRINT(\"%02X \", serial_data[i]);\n                }\n                DEBUG_PRINT(\"\\n\");\n                write(serial_fd, &serial_data, serial_data_size);\n            } else {\n                // Otherwise, queue it up for processing by the MSP layer.\n                DEBUG_PRINT(\"SEND data to MSP buffer! length %d\\n\", serial_data_size);\n                for (ssize_t i = 0; i < serial_data_size; i++) {\n                    msp_process_data(tx_msp_state, serial_data[i]);\n                }\n            }\n        }\n        clock_gettime(CLOCK_MONOTONIC, &now);\n        if(timespec_subtract_ns(&now, &last_data) > (NSEC_PER_SEC / 2)) {\n            // More than 500ms have elapsed, let's go ahead and send a data frame\n            clock_gettime(CLOCK_MONOTONIC, &last_data);\n            send_data_packet(data_fd, &dji_radio);\n            if(current_fc_identifier[0] == 0) {\n                send_variant_request(serial_fd);\n            }\n        }\n        if(compress && (timespec_subtract_ns(&now, &last_frame) > (NSEC_PER_SEC / update_rate_hz))) {\n            send_compressed_screen(compressed_fd);\n            clock_gettime(CLOCK_MONOTONIC, &last_frame);\n        }\n    }\n    close_dji_radio_shm(&dji_radio);\n    close(serial_fd);\n    close(pty_fd);\n    close(socket_fd);\n    close(data_fd);\n    close(compressed_fd);\n    if (display_driver != NULL) {\n        free(display_driver);\n    }\n    if (lz4_ref_ctx != NULL) {\n        free(lz4_ref_ctx);\n    }\n    free(rx_msp_state);\n    free(tx_msp_state);\n}\n"
  },
  {
    "path": "jni/net/data_protocol.h",
    "content": "#include <stdint.h>\n\ntypedef struct packet_data_s {\n    uint16_t tx_temperature;\n    uint16_t version_specifier; // Used to be bitrate! Danger! 0xFFFF means V2 (no bitrate) for now.\n    uint16_t tx_voltage;\n    char fc_variant[4];\n} __attribute__((packed)) packet_data_t;\n\ntypedef struct compressed_data_header_s {\n    uint16_t version;\n    uint16_t hd_options;\n} __attribute__((packed)) compressed_data_header_t;"
  },
  {
    "path": "jni/net/network.c",
    "content": "#include <arpa/inet.h>\n#include <sys/socket.h>\n#include <netinet/in.h>\n#include <fcntl.h>\n#include <stdio.h>\n#include <string.h>\n\n#include \"network.h\"\n\nint connect_to_server(char *address, int port)\n{\n    int sockfd;\n    struct sockaddr_in servaddr;\n\n    // socket create and verification\n    sockfd = socket(AF_INET, SOCK_DGRAM, 0);\n    if (sockfd == -1)\n    {\n        printf(\"socket failed!\\n\");\n        return -1;\n    }\n\n    memset(&servaddr, 0, sizeof(servaddr));\n\n    // assign IP, PORT\n    servaddr.sin_family = AF_INET;\n    servaddr.sin_addr.s_addr = inet_addr(address);\n    servaddr.sin_port = htons(port);\n\n    if (connect(sockfd, &servaddr, sizeof(servaddr)) != 0)\n    {\n        printf(\"connection failed!\\n\");\n        return -1;\n    }\n\n    fcntl(sockfd, F_SETFL, O_NONBLOCK);\n    return sockfd;\n}\n\nint bind_socket(int port)\n{\n    struct sockaddr_in si_me;\n    int s;\n    if ((s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1)\n    {\n        printf(\"Failed to get socket!\\n\");\n        return -1;\n    }\n\n    memset((char *)&si_me, 0, sizeof(si_me));\n\n    si_me.sin_family = AF_INET;\n    si_me.sin_port = htons(port);\n    si_me.sin_addr.s_addr = htonl(INADDR_ANY);\n\n    // bind socket to port\n    if (bind(s, (struct sockaddr *)&si_me, sizeof(si_me)) == -1)\n    {\n        printf(\"Failed to get bound!\\n\");\n        return -1;\n    }\n    return s;\n}"
  },
  {
    "path": "jni/net/network.h",
    "content": "#pragma once\n\nint connect_to_server(char *address, int port);\nint bind_socket(int port);"
  },
  {
    "path": "jni/net/serial.c",
    "content": "#include <stdio.h>\n#include <arpa/inet.h>\n#include <sys/socket.h>\n#include <netinet/in.h>\n#include <unistd.h>\n#include <fcntl.h>\n#include <stdlib.h>\n#include <string.h>\n#include <termios.h>\n#ifdef __APPLE__\n#include <util.h>\n#endif\n#include \"serial.h\"\n\nint open_serial_port(const char *device, speed_t baudrate)\n{\n    struct termios tio;\n    int tty_fd;\n\n    memset(&tio, 0, sizeof(tio));\n    tio.c_iflag = 0;\n    tio.c_oflag = 0;\n    tio.c_cflag = CS8 | CREAD | CLOCAL;\n    tio.c_lflag = 0;\n    tio.c_cc[VMIN] = 1;\n    tio.c_cc[VTIME] = 0;\n\n    tty_fd = open(device, O_RDWR | O_NONBLOCK);\n    cfsetospeed(&tio, baudrate);\n    cfsetispeed(&tio, baudrate);\n    tcsetattr(tty_fd, TCSANOW, &tio);\n    return tty_fd;\n}\n\nint open_pty(const char **pty_name)\n{\n    int app_pty;\n    int virtual_serial_pty;\n\n    // openpty makes a connection between the first argument, a parent FD which acts as the \"receiving\" end of the virtual PTY\n    // and the second argument, a child FD which represents a \"virtual serial port.\"\n    // using ttyname on the second argument will return a /dev which can be opened as a serial port\n    pid_t pid = openpty(&app_pty, &virtual_serial_pty, NULL, NULL, NULL);\n\n    if (pid != 0)\n    {\n        printf(\"Could not get PTY!\");\n        exit(1);\n        return pid;\n    }\n\n    (*pty_name) = ttyname(virtual_serial_pty);\n    struct termios orig_termios;\n    if (tcgetattr(app_pty, &orig_termios) < 0)\n    {\n        printf(\"Could not get termio for current FD\");\n        return -1;\n    }\n\n    orig_termios.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);\n    orig_termios.c_oflag &= ~(OPOST);\n    orig_termios.c_cflag |= (CS8);\n    orig_termios.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);\n    orig_termios.c_cc[VMIN] = 0;\n    orig_termios.c_cc[VTIME] = 1;\n\n    if (tcsetattr(app_pty, TCSANOW, &orig_termios) < 0)\n    {\n        perror(\"Could not set termio for current FD\");\n        return -1;\n    }    \n\n    if (tcgetattr(virtual_serial_pty, &orig_termios) < 0)\n    {\n        printf(\"Could not get termio for current FD\");\n        return -1;\n    }\n\n    orig_termios.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);\n    orig_termios.c_oflag &= ~(OPOST);\n    orig_termios.c_cflag |= (CS8);\n    orig_termios.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);\n    orig_termios.c_cc[VMIN] = 0;\n    orig_termios.c_cc[VTIME] = 1;\n\n    if (tcsetattr(virtual_serial_pty, TCSANOW, &orig_termios) < 0)\n    {\n        perror(\"Could not set termio for current FD\");\n        return -1;\n    }    \n    fcntl(app_pty, F_SETFL, O_NONBLOCK);\n    return app_pty; // Return the file descriptor\n}\n\n#ifdef DEBUG_TRACE\nvoid _trace(const char* format, ...);\n#endif\n\n#ifdef DEBUG_TRACE\n#define TRACE(X) _trace X;\n#else /*DEBUG_TRACE*/\n#define TRACE(X)\n#endif /*DEBUG_TRACE*/\n#ifdef __ANDROID__\nint\nopenpty (int *amaster, int *aslave, char *name, struct termios *termp,\n         struct winsize *winp)\n{\n  int master, slave;\n  char *name_slave;\n\n  master = open(\"/dev/ptmx\", O_RDWR | O_NONBLOCK);\n  if (master == -1) {\n    TRACE((\"Fail to open master\"))\n    return -1;\n  }\n\n  if (grantpt(master))\n    goto fail;\n\n  if (unlockpt(master))\n    goto fail;\n\n  name_slave = ptsname(master);\n  TRACE((\"openpty: slave name %s\", name_slave))\n  slave = open(name_slave, O_RDWR | O_NOCTTY);\n  if (slave == -1)\n    {\n      goto fail;\n    }\n\n  if(termp)\n    tcsetattr(slave, TCSAFLUSH, termp);\n  if (winp)\n    ioctl (slave, TIOCSWINSZ, winp);\n\n  *amaster = master;\n  *aslave = slave;\n  if (name != NULL)\n    strcpy(name, name_slave);\n\n  return 0;\n\n fail:\n  close (master);\n  return -1;\n}\n#endif"
  },
  {
    "path": "jni/net/serial.h",
    "content": "#pragma once\n\n#include <termios.h>\n\nint open_serial_port(const char *device, speed_t baudrate);\nint open_pty(const char **pty_name);\n#ifdef __ANDROID__\nint\nopenpty (int *amaster, int *aslave, char *name, struct termios *termp,\n         struct winsize *winp);\n#endif"
  },
  {
    "path": "jni/osd.h",
    "content": "#pragma once\n\n#include \"hw/dji_display.h\"\n\nvoid osd_directfb(duss_disp_instance_handle_t *disp, duss_hal_obj_handle_t ion_handle);\nvoid osd_disable();\nvoid osd_enable();"
  },
  {
    "path": "jni/osd_dji_overlay_udp.c",
    "content": "#include <stdio.h>\n#include <arpa/inet.h>\n#include <sys/socket.h>\n#include <netinet/in.h>\n#include <unistd.h>\n#include <fcntl.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/mman.h>\n#include <sys/types.h>\n#include <unistd.h>\n#include <sys/stat.h>\n#include <assert.h>\n#include <sys/poll.h>\n#include <time.h>\n#include <linux/input.h>\n#include <sys/eventfd.h>\n\n#include \"hw/dji_display.h\"\n#include \"hw/dji_radio_shm.h\"\n#include \"hw/dji_services.h\"\n#include \"json/osd_config.h\"\n#include \"fakehd/fakehd.h\"\n#include \"font/font.h\"\n#include \"lz4/lz4.h\"\n#include \"toast/toast.h\"\n#include \"net/network.h\"\n#include \"net/data_protocol.h\"\n#include \"msp/msp.h\"\n#include \"msp/msp_displayport.h\"\n\n#include \"util/debug.h\"\n#include \"util/display_info.h\"\n#include \"util/fs_util.h\"\n#include \"util/time_util.h\"\n#include \"rec/rec.h\"\n#include \"rec/rec_pb.h\"\n\n#define MSP_PORT 7654\n#define DATA_PORT 7655\n#define COMPRESSED_DATA_PORT 7656\n#define DICTIONARY_VERSION 1\n\n#define WIDTH 1440\n#define HEIGHT 810\n#define BYTES_PER_PIXEL 4\n#define PLANE_ID 6\n\n#define GOGGLES_V1_VOFFSET 575\n#define GOGGLES_V2_VOFFSET 215\n\n#define INPUT_FILENAME \"/dev/input/event0\"\n#define SPLASH_STRING \"OSD WAITING...\"\n#define SHUTDOWN_STRING \"SHUTTING DOWN...\"\n#define SPLASH_KEY \"show_waiting\"\n\n#define FORCE_RENDER_HZ 2\n#define TOAST_HZ 2\n\n#define GOGGLES_VOLTAGE_PATH \"/sys/devices/platform/soc/f0a00000.apb/f0a71000.omc/voltage5\"\n\n#define EV_CODE_BACK 0xc9\n\n#define BACK_BUTTON_DELAY 4\n\n#define SWAP32(data)   \\\n( (((data) >> 24) & 0x000000FF) | (((data) >>  8) & 0x0000FF00) | \\\n  (((data) <<  8) & 0x00FF0000) | (((data) << 24) & 0xFF000000) )\n\n#define MAX_DISPLAY_X 60\n#define MAX_DISPLAY_Y 22\n\nstatic void rec_msp_draw_complete_hook();\n\nstatic volatile sig_atomic_t quit = 0;\nstatic dji_display_state_t *dji_display;\nstatic uint16_t msp_character_map[MAX_DISPLAY_X][MAX_DISPLAY_Y];\nstatic uint16_t msp_render_character_map[MAX_DISPLAY_X][MAX_DISPLAY_Y];\nstatic uint16_t overlay_character_map[MAX_DISPLAY_X][MAX_DISPLAY_Y];\nstatic displayport_vtable_t *display_driver;\nstruct timespec last_render;\n\nstatic char current_fc_variant[5];\n\nstatic uint8_t frame_waiting = 0;\n\nstatic display_info_t sd_display_info = {\n    .char_width = 30,\n    .char_height = 15,\n    .font_width = 36,\n    .font_height = 54,\n    .x_offset = 180,\n    .y_offset = 0,\n    .fonts = {NULL, NULL, NULL, NULL},\n};\n\nstatic display_info_t full_display_info = {\n    .char_width = 60,\n    .char_height = 22,\n    .font_width = 24,\n    .font_height = 36,\n    .x_offset = 0,\n    .y_offset = 0,\n    .fonts = {NULL, NULL, NULL, NULL},\n};\n\nstatic display_info_t hd_display_info = {\n    .char_width = 50,\n    .char_height = 18,\n    .font_width = 24,\n    .font_height = 36,\n    .x_offset = 120,\n    .y_offset = 80,\n    .fonts = {NULL, NULL, NULL, NULL},\n};\n\nstatic display_info_t overlay_display_info = {\n    .char_width = 20,\n    .char_height = 10,\n    .font_width = 24,\n    .font_height = 36,\n    .x_offset = 960,\n    .y_offset = 450,\n    .fonts = {NULL, NULL, NULL, NULL},\n};\n\nstatic enum display_mode_s {\n        DISPLAY_DISABLED = 0,\n        DISPLAY_RUNNING = 1,\n        DISPLAY_WAITING = 2\n} display_mode = DISPLAY_RUNNING;\n\nstatic display_info_t *current_display_info;\n\nint event_fd;\n\nstatic void draw_character(display_info_t *display_info, uint16_t character_map[MAX_DISPLAY_X][MAX_DISPLAY_Y], uint32_t x, uint32_t y, uint16_t c)\n{\n    if ((x > (display_info->char_width - 1)) || (y > (display_info->char_height - 1))) {\n        return;\n    }\n    character_map[x][y] = c;\n}\n\nstatic void msp_draw_character(uint32_t x, uint32_t y, uint16_t c) {\n    draw_character(current_display_info, msp_character_map, x, y, c);\n}\n\n/* Main rendering function: take a character_map and a display_info and draw it into a framebuffer */\n\nstatic void draw_character_map(display_info_t *display_info, void* restrict fb_addr, uint16_t character_map[MAX_DISPLAY_X][MAX_DISPLAY_Y]) {\n    if (display_info->fonts[0] == NULL) {\n        DEBUG_PRINT(\"No font available, failed to draw.\\n\");\n        return;\n    }\n    void* restrict font;\n    for(int y = 0; y < display_info->char_height; y++) {\n        for(int x = 0; x < display_info->char_width; x++) {\n            uint16_t c = character_map[x][y];\n            if (c != 0) {\n                int page = (c & 0x300) >> 8;\n                c = c & 0xFF;\n                font = display_info->fonts[page];\n                if(font == NULL) {\n                    font = display_info->fonts[0];\n                }\n                uint32_t pixel_x = (x * display_info->font_width) + display_info->x_offset;\n                uint32_t pixel_y = (y * display_info->font_height) + display_info->y_offset;\n                uint32_t font_offset = (((display_info->font_height * display_info->font_width) * BYTES_PER_PIXEL) * c);\n                uint32_t target_offset = ((pixel_x * BYTES_PER_PIXEL) + (pixel_y * WIDTH * BYTES_PER_PIXEL));\n                for(uint8_t gy = 0; gy < display_info->font_height; gy++) {\n                    for(uint8_t gx = 0; gx < display_info->font_width; gx++) {\n                        *((uint8_t *)fb_addr + target_offset) = *(uint8_t *)((uint8_t *)font + font_offset + 2);\n                        *((uint8_t *)fb_addr + target_offset + 1) = *(uint8_t *)((uint8_t *)font + font_offset + 1);\n                        *((uint8_t *)fb_addr + target_offset + 2) = *(uint8_t *)((uint8_t *)font + font_offset);\n                        *((uint8_t *)fb_addr + target_offset + 3) = ~*(uint8_t *)((uint8_t *)font + font_offset + 3);\n                        font_offset += BYTES_PER_PIXEL;\n                        target_offset += BYTES_PER_PIXEL;\n                    }\n                    target_offset += WIDTH * BYTES_PER_PIXEL - (display_info->font_width * BYTES_PER_PIXEL);\n                }\n                // DEBUG_PRINT(\"%c\", c > 31 ? c : 20);\n            }\n            // DEBUG_PRINT(\" \");\n        }\n        // DEBUG_PRINT(\"\\n\");\n    }\n}\n\nstatic void clear_framebuffer() {\n    void *fb_addr = dji_display_get_fb_address(dji_display);\n    // DJI has a backwards alpha channel - FF is transparent, 00 is opaque.\n    memset(fb_addr, 0x000000FF, WIDTH * HEIGHT * BYTES_PER_PIXEL);\n}\n\nstatic void draw_screen() {\n    clear_framebuffer();\n\n    void *fb_addr = dji_display_get_fb_address(dji_display);\n\n    if (fakehd_is_enabled()) {\n        fakehd_map_sd_character_map_to_hd(msp_character_map, msp_render_character_map);\n        draw_character_map(current_display_info, fb_addr, msp_render_character_map);\n    } else {\n        draw_character_map(current_display_info, fb_addr, msp_character_map);\n    }\n    draw_character_map(&overlay_display_info, fb_addr, overlay_character_map);\n}\n\nstatic void clear_overlay() {\n    memset(overlay_character_map, 0, sizeof(overlay_character_map));\n}\n\nstatic void msp_clear_screen() {\n    memset(msp_character_map, 0, sizeof(msp_character_map));\n    memset(msp_render_character_map, 0, sizeof(msp_render_character_map));\n}\n\nstatic void render_screen() {\n    draw_screen();\n    if (display_mode == DISPLAY_DISABLED) {\n        clear_framebuffer();\n    }\n    dji_display_push_frame(dji_display);\n    DEBUG_PRINT(\"drew a frame\\n\");\n    clock_gettime(CLOCK_MONOTONIC, &last_render);\n}\n\nstatic void msp_draw_complete() {\n    render_screen();\n\n    if (rec_is_enabled()) {\n        rec_msp_draw_complete_hook();\n        if (rec_is_osd_recording() == true)\n        {\n            rec_write_frame(\n                fakehd_is_enabled() ? msp_render_character_map : msp_character_map,\n                MAX_DISPLAY_X * MAX_DISPLAY_Y);\n        }\n    }\n}\n\nstatic void msp_callback(msp_msg_t *msp_message)\n{\n    displayport_process_message(display_driver, msp_message);\n}\n\nstatic void load_fonts(char* font_variant) {\n    char file_path[255];\n    get_font_path_with_extension(file_path, \"font\", \".png\", 255, 0, font_variant);\n    toast(file_path);\n    load_font(&sd_display_info, font_variant);\n    load_font(&hd_display_info, font_variant);\n    load_font(&full_display_info, font_variant);\n    load_font(&overlay_display_info, font_variant);\n}\n\nstatic void close_all_fonts() {\n    close_font(&sd_display_info);\n    close_font(&hd_display_info);\n    close_font(&overlay_display_info);\n    close_font(&full_display_info);\n}\n\nstatic void msp_set_options(uint8_t font_num, msp_hd_options_e is_hd) {\n    msp_clear_screen();\n\n    switch (is_hd) {\n        case MSP_HD_OPTION_60_22:\n            fakehd_disable();\n            current_display_info = &full_display_info;\n            break;\n        case MSP_HD_OPTION_50_18:\n        case MSP_HD_OPTION_30_16:\n            fakehd_disable();\n            current_display_info = &hd_display_info;\n            break;\n        default:\n            current_display_info = &sd_display_info;\n            break;\n    }\n}\n\nstatic void display_print_string(uint8_t init_x, uint8_t y, const char *string, uint8_t len) {\n    for(uint8_t x = 0; x < len; x++) {\n        draw_character(&overlay_display_info, overlay_character_map, x + init_x, y, string[x]);\n    }\n}\n\n/* Display initialization and deinitialization */\n\nstatic void start_display(uint8_t is_v2_goggles,duss_disp_instance_handle_t *disp, duss_hal_obj_handle_t ion_handle) {\n    memset(msp_character_map, 0, sizeof(msp_character_map));\n    memset(msp_render_character_map, 0, sizeof(msp_render_character_map));\n    memset(overlay_character_map, 0, sizeof(overlay_character_map));\n\n    dji_display = dji_display_state_alloc(is_v2_goggles);\n    dji_display_open_framebuffer_injected(dji_display, disp, ion_handle, PLANE_ID);\n    if(get_boolean_config_value(SPLASH_KEY)) {\n        display_print_string(0, overlay_display_info.char_height -1, SPLASH_STRING, sizeof(SPLASH_STRING));\n    }\n    msp_draw_complete();\n}\n\nstatic void stop_display() {\n    display_print_string(0, overlay_display_info.char_height -1, SHUTDOWN_STRING, sizeof(SHUTDOWN_STRING));\n    dji_display_close_framebuffer(dji_display);\n    dji_display_state_free(dji_display);\n}\n\n/* AU Voltage and Temp overlay */\n\n#define SHOW_AU_DATA_KEY \"show_au_data\"\nstatic int au_overlay_enabled = 0;\n\nstatic void check_is_au_overlay_enabled()\n{\n    DEBUG_PRINT(\"Checking for AU overlay enabled: \");\n    if (get_boolean_config_value(SHOW_AU_DATA_KEY))\n    {\n        DEBUG_PRINT(\"Enabled\\n\");\n        au_overlay_enabled = 1;\n    } else {\n        DEBUG_PRINT(\"Disabled\\n\");\n    }\n}\n\nstatic void process_data_packet(uint8_t *buf, int len, dji_shm_state_t *radio_shm) {\n    packet_data_t *packet = (packet_data_t *)buf;\n    DEBUG_PRINT(\"got data %04X version spec %d C %f V variant %.4s\\n\", packet->version_specifier, packet->tx_temperature, packet->tx_voltage / 64.0f, packet->fc_variant);\n    char str[8];\n    clear_overlay();\n    if(au_overlay_enabled) {\n        snprintf(str, 8, \"%d C\", packet->tx_temperature);\n        display_print_string(overlay_display_info.char_width - 5, overlay_display_info.char_height - 8, str, 5);\n        snprintf(str, 8, \"A %2.1fV\", packet->tx_voltage / 64.0f);\n        display_print_string(overlay_display_info.char_width - 7, overlay_display_info.char_height - 7, str, 7);\n    }\n    if(len > 6) {\n        DEBUG_PRINT(\"Got new packet with variant %.4s\\n\", packet->fc_variant);\n        // should have FC type\n        if(strncmp(current_fc_variant, packet->fc_variant, 4) != 0) {\n            memcpy(current_fc_variant, &packet->fc_variant, 4);\n            DEBUG_PRINT(\"Changed current FC variant to %.4s\\n\", current_fc_variant);\n            toast(\"FC %.4s\", current_fc_variant);\n            close_all_fonts();\n            load_fonts(current_fc_variant);\n            // This is not a typo - fill in any missing fonts for the current variant with the generic one.\n            load_fonts(FALLBACK_FONT);\n        }\n    }\n}\n\nstatic void process_compressed_data(void *buf, int len, void *dict, int dict_size) {\n    compressed_data_header_t *header = (compressed_data_header_t*)buf;\n    if (header->version != DICTIONARY_VERSION) {\n        return;\n    }\n    switch ((msp_hd_options_e)header->hd_options) {\n        case MSP_HD_OPTION_60_22:\n            fakehd_disable();\n            current_display_info = &full_display_info;\n            break;\n        case MSP_HD_OPTION_30_16:\n        case MSP_HD_OPTION_50_18:\n            fakehd_disable();\n            current_display_info = &hd_display_info;\n            break;\n        default:\n            if (fakehd_is_enabled()) {\n                memset(msp_render_character_map, 0, sizeof(msp_render_character_map));\n                current_display_info = &full_display_info;\n            } else {\n                current_display_info = &sd_display_info;\n            }\n            break;\n    }\n    int decompressed_size = LZ4_decompress_safe_usingDict((buf + sizeof(compressed_data_header_t)), msp_character_map, len - sizeof(compressed_data_header_t), sizeof(msp_character_map), dict, dict_size);\n    DEBUG_PRINT(\"Decompressed %d bytes!\\n\", decompressed_size);\n    msp_draw_complete();\n}\n\n/* Recording hooks */\n\nstatic void rec_msp_draw_complete_hook()\n{\n    if (rec_is_osd_recording() == false && rec_is_gls_recording() == true)\n    {\n        if (current_fc_variant[0] == '\\0')\n        {\n            DEBUG_PRINT(\"msp_osd: gls started recording, but no fc variant yet!?\\n\");\n            return;\n        }\n\n        DEBUG_PRINT(\"msp_osd: gls started recording, start osd rec\\n\");\n\n        rec_config_t config = {\n            .char_width = current_display_info->char_width,\n            .char_height = current_display_info->char_height,\n            .font_width = current_display_info->font_width,\n            .font_height = current_display_info->font_height,\n            .x_offset = current_display_info->x_offset,\n            .y_offset = current_display_info->y_offset\n        };\n        strcpy(config.font_variant, current_fc_variant);\n\n        rec_start(&config);\n    }\n    else if (rec_is_osd_recording() == true && rec_is_gls_recording() == false)\n    {\n        DEBUG_PRINT(\"msp_osd: gls stopped recording, stop osd rec\\n\");\n        rec_stop();\n    }\n}\n\nstatic void rec_pb_play_loop()\n{\n    struct timespec last;\n    clock_gettime(CLOCK_MONOTONIC, &last);\n\n    int64_t target_diff = NSEC_PER_SEC / 15; // 15 fps\n    int64_t next_diff = target_diff;\n\n    while (rec_pb_gls_is_playing())\n    {\n        struct timespec now;\n        clock_gettime(CLOCK_MONOTONIC, &now);\n        int64_t diff_ns = timespec_subtract_ns(&now, &last);\n\n        if (diff_ns >= next_diff)\n        {\n            rec_pb_do_next_frame((uint16_t *) msp_character_map);\n            render_screen();\n\n            next_diff = target_diff + (target_diff - diff_ns);\n            last = now;\n        }\n    }\n\n    rec_pb_stop();\n}\n\nstatic void rec_pb_timeout_hook()\n{\n    uint8_t is_playing = rec_pb_gls_is_playing();\n\n    // Only try start once per playback.\n    if (is_playing == true && rec_pb_start_attempted == true) {\n        return;\n    } else if (is_playing == true) {\n        rec_pb_start_attempted = true;\n    } else {\n        rec_pb_start_attempted = false;\n    }\n\n    if (is_playing == true) {\n        DEBUG_PRINT(\"msp_osd: gls playing dvr, let's try too!\\n\");\n\n        if (rec_pb_start() != 0)\n        {\n            DEBUG_PRINT(\"msp_osd: failed to start playback!\\n\");\n            return;\n        }\n\n        rec_config_t *rec_config = rec_pb_get_config();\n        display_info_t *osd_display_info = malloc(sizeof(display_info_t));\n        memset(osd_display_info, 0, sizeof(display_info_t));\n\n        osd_display_info->char_width = rec_config->char_width;\n        osd_display_info->char_height = rec_config->char_height;\n        osd_display_info->font_width = rec_config->font_width;\n        osd_display_info->font_height = rec_config->font_height;\n        osd_display_info->x_offset = rec_config->x_offset;\n        osd_display_info->y_offset = rec_config->y_offset;\n\n        DEBUG_PRINT(\"msp_osd: playback config, char_width: %d\\n\", osd_display_info->char_width);\n        DEBUG_PRINT(\"msp_osd: playback config, char_height: %d\\n\", osd_display_info->char_height);\n        DEBUG_PRINT(\"msp_osd: playback config, font_width: %d\\n\", osd_display_info->font_width);\n        DEBUG_PRINT(\"msp_osd: playback config, font_height: %d\\n\", osd_display_info->font_height);\n        DEBUG_PRINT(\"msp_osd: playback config, x_offset: %d\\n\", osd_display_info->x_offset);\n        DEBUG_PRINT(\"msp_osd: playback config, y_offset: %d\\n\", osd_display_info->y_offset);\n\n        DEBUG_PRINT(\"msp_osd: gls playing dvr, loading font variant %s\\n\", rec_config->font_variant);\n        uint8_t is_hd = osd_display_info->font_width != sd_display_info.font_width;\n        load_font(osd_display_info, rec_config->font_variant);\n\n        // TODO: Sketchy swap here?\n        // Might end playback after swapping channel, maybe? So back on live channel but with\n        // DISPLAY_DISABLED which would be bad. :(\n        current_display_info = osd_display_info;\n        clear_overlay();\n        display_mode = DISPLAY_RUNNING;\n\n        uint8_t fakehd_was_enabled = fakehd_is_enabled();\n        if (fakehd_was_enabled == true) {\n            fakehd_disable();\n        }\n\n        rec_pb_play_loop();\n\n        current_display_info = &sd_display_info;\n        memset(msp_character_map, 0, sizeof(msp_character_map));\n        display_mode = DISPLAY_DISABLED;\n\n        if (fakehd_was_enabled == true) {\n            fakehd_enable();\n        }\n\n        close_font(osd_display_info);\n        free(osd_display_info);\n    }\n}\n\n/* Public OSD enable/disable methods */\n\nvoid osd_disable() {\n    uint64_t disable = 1;\n    write(event_fd, &disable, sizeof(uint64_t));\n}\n\nvoid osd_enable() {\n    uint64_t enable = 2;\n    write(event_fd, &enable, sizeof(uint64_t));\n}\n\n/* Entry point and main loop */\n\nvoid osd_directfb(duss_disp_instance_handle_t *disp, duss_hal_obj_handle_t ion_handle)\n{\n    memset(current_fc_variant, 0, sizeof(current_fc_variant));\n\n    toast_load_config();\n    load_fakehd_config();\n    rec_load_config();\n    rec_pb_load_config();\n    check_is_au_overlay_enabled();\n\n    uint8_t is_v2_goggles = dji_goggles_are_v2();\n    DEBUG_PRINT(\"Detected DJI goggles %s\\n\", is_v2_goggles ? \"V2\" : \"V1\");\n\n    if (fakehd_is_enabled())\n    {\n        current_display_info = &full_display_info;\n    } else {\n        current_display_info = &sd_display_info;\n    }\n\n    display_driver = calloc(1, sizeof(displayport_vtable_t));\n    display_driver->draw_character = &msp_draw_character;\n    display_driver->clear_screen = &msp_clear_screen;\n    display_driver->draw_complete = &msp_draw_complete;\n    display_driver->set_options = &msp_set_options;\n\n    msp_state_t *msp_state = calloc(1, sizeof(msp_state_t));\n    msp_state->cb = &msp_callback;\n\n    event_fd = eventfd(0, 0);\n    assert(event_fd > 0);\n\n    dji_shm_state_t radio_shm;\n    memset(&radio_shm, 0, sizeof(radio_shm));\n\n    int msp_socket_fd = bind_socket(MSP_PORT);\n    int data_socket_fd = bind_socket(DATA_PORT);\n    int compressed_socket_fd = bind_socket(COMPRESSED_DATA_PORT);\n    printf(\"*** MSP-OSD: MSP-OSD started up, listening on port %d\\n\", MSP_PORT);\n\n    struct pollfd poll_fds[4];\n    int recv_len = 0;\n    uint8_t byte = 0;\n    uint8_t buffer[8192];\n    struct sockaddr_storage src_addr;\n    socklen_t src_addr_len=sizeof(src_addr);\n    struct input_event ev;\n    struct timespec now, last_toast;\n    memset(&last_toast, 0, sizeof(last_toast));\n    memset(&last_render, 0, sizeof(last_render));\n    memset(&now, 0, sizeof(now));\n\n    load_fonts(FALLBACK_FONT);\n    open_dji_radio_shm(&radio_shm);\n    start_display(is_v2_goggles, disp, ion_handle);\n\n    int compression_dict_size = 0;\n    void *compression_dict = open_dict(DICTIONARY_VERSION, &compression_dict_size);\n\n    uint64_t event_number;\n    while (!quit)\n    {\n        poll_fds[0].fd = msp_socket_fd;\n        poll_fds[0].events = POLLIN;\n        poll_fds[1].fd = event_fd;\n        poll_fds[1].events = POLLIN;\n        poll_fds[2].fd = data_socket_fd;\n        poll_fds[2].events = POLLIN;\n        poll_fds[3].fd = compressed_socket_fd;\n        poll_fds[3].events = POLLIN;\n\n        // spin every 250ms if we didn't get a packet, then check and see if we need to do the toast/overlay logic\n        poll(poll_fds, 4, 250);\n\n        clock_gettime(CLOCK_MONOTONIC, &now);\n\n        if(poll_fds[0].revents) {\n            // Got MSP UDP packet\n            if (0 < (recv_len = recvfrom(msp_socket_fd,&buffer,sizeof(buffer),0,(struct sockaddr*)&src_addr,&src_addr_len)))\n            {\n                DEBUG_PRINT(\"got MSP packet len %d\\n\", recv_len);\n                if(display_mode == DISPLAY_RUNNING) {\n                    for (int i=0; i<recv_len; i++)\n                        msp_process_data(msp_state, buffer[i]);\n                }\n            }\n        }\n        if(poll_fds[2].revents) {\n            // Got data UDP packet\n            if (0 < (recv_len = recvfrom(data_socket_fd,&buffer,sizeof(buffer),0,(struct sockaddr*)&src_addr,&src_addr_len)))\n            {\n                DEBUG_PRINT(\"got DATA packet len %d\\n\", recv_len);\n                if(display_mode == DISPLAY_RUNNING) {\n                    process_data_packet(buffer, recv_len, &radio_shm);\n                }\n            }\n        }\n        if(poll_fds[1].revents) {\n            // Got eventfd message from another thread to enable/disable OSD\n            if (0 < (recv_len = read(event_fd, &event_number, sizeof(uint64_t)))) {\n                if(event_number > 1) {\n                    DEBUG_PRINT(\"Display running transition\\n\");\n                    display_mode = DISPLAY_RUNNING;\n                } else {\n                    DEBUG_PRINT(\"Display disabled transition\\n\");\n                    display_mode = DISPLAY_DISABLED;\n                }\n            }\n        }\n\n        if(poll_fds[3].revents) {\n            // Got compressed data\n            if (0 < (recv_len = recvfrom(compressed_socket_fd,&buffer,sizeof(buffer),0,(struct sockaddr*)&src_addr,&src_addr_len)))\n            {\n                DEBUG_PRINT(\"got COMPRESSED data packet len %d\\n\", recv_len);\n                if(display_mode == DISPLAY_RUNNING) {\n                    process_compressed_data(buffer, recv_len, compression_dict, compression_dict_size);\n                }\n            }\n        }\n\n        if(timespec_subtract_ns(&now, &last_toast) > (NSEC_PER_SEC / TOAST_HZ)) {\n            // lets toast run + update any notices\n            do_toast(display_print_string);\n            clock_gettime(CLOCK_MONOTONIC, &last_toast);\n        }\n\n        if(timespec_subtract_ns(&now, &last_render) > (NSEC_PER_SEC / FORCE_RENDER_HZ)) {\n            // More than 500ms have elapsed without a render, let's go ahead and manually render\n            render_screen();\n        }\n\n        if (rec_pb_is_enabled()) {\n            rec_pb_timeout_hook();\n        }\n    }\n\n    free(display_driver);\n    free(msp_state);\n    free(compression_dict);\n    close(msp_socket_fd);\n    close(data_socket_fd);\n    close(compressed_socket_fd);\n    close(event_fd);\n    return;\n}\n"
  },
  {
    "path": "jni/osd_dji_udp.c",
    "content": "#include <stdio.h>\n#include <arpa/inet.h>\n#include <sys/socket.h>\n#include <netinet/in.h>\n#include <unistd.h>\n#include <fcntl.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/mman.h>\n#include <sys/types.h>\n#include <unistd.h>\n#include <sys/stat.h>\n#include <assert.h>\n#include <sys/poll.h>\n#include <time.h>\n#include <linux/input.h>\n\n#include \"hw/dji_display.h\"\n#include \"hw/dji_radio_shm.h\"\n#include \"hw/dji_services.h\"\n#include \"net/network.h\"\n#include \"net/data_protocol.h\"\n#include \"msp/msp.h\"\n#include \"msp/msp_displayport.h\"\n#include \"util/debug.h\"\n#include \"util/fs_util.h\"\n\n#define MSP_PORT 7654\n#define DATA_PORT 7655\n\n#define WIDTH 1440\n#define HEIGHT 810\n#define BYTES_PER_PIXEL 4\n#define PLANE_ID 6\n\n#define NUM_CHARS 256\n\n#define INPUT_FILENAME \"/dev/input/event0\"\n#define SPLASH_STRING \"OSD WAITING...\"\n#define SHUTDOWN_STRING \"SHUTTING DOWN...\"\n\n#define FALLBACK_FONT_PATH \"/blackbox/font\"\n#define ENTWARE_FONT_PATH \"/opt/fonts/font\"\n#define SDCARD_FONT_PATH \"/storage/sdcard0/font\"\n\n#define GOGGLES_VOLTAGE_PATH \"/sys/devices/platform/soc/f0a00000.apb/f0a71000.omc/voltage5\"\n\n#define EV_CODE_BACK 0xc9\n\n#define BACK_BUTTON_DELAY 4\n\n#define SWAP32(data)   \\\n( (((data) >> 24) & 0x000000FF) | (((data) >>  8) & 0x0000FF00) | \\\n  (((data) <<  8) & 0x00FF0000) | (((data) << 24) & 0xFF000000) )\n\n#define MAX_DISPLAY_X 50\n#define MAX_DISPLAY_Y 18\n\ntypedef struct display_info_s {\n    uint8_t char_width;\n    uint8_t char_height;\n    uint8_t font_width;\n    uint8_t font_height;\n    uint16_t x_offset;\n    uint16_t y_offset;\n    void *font_page_1;\n    void *font_page_2;\n} display_info_t;\n\nstatic volatile sig_atomic_t quit = 0;\nstatic dji_display_state_t *dji_display;\nstatic uint16_t msp_character_map[MAX_DISPLAY_X][MAX_DISPLAY_Y];\nstatic uint16_t overlay_character_map[MAX_DISPLAY_X][MAX_DISPLAY_Y];\nstatic displayport_vtable_t *display_driver;\nstatic uint8_t which_fb = 0;\n\nstatic display_info_t sd_display_info = {\n    .char_width = 31,\n    .char_height = 15,\n    .font_width = 36,\n    .font_height = 54,\n    .x_offset = 180,\n    .y_offset = 0,\n    .font_page_1 = NULL,\n    .font_page_2 = NULL,\n};\n\nstatic display_info_t hd_display_info = {\n    .char_width = 50,\n    .char_height = 18,\n    .font_width = 24,\n    .font_height = 36,\n    .x_offset = 120,\n    .y_offset = 80,\n    .font_page_1 = NULL,\n    .font_page_2 = NULL,\n};\n\nstatic display_info_t overlay_display_info = {\n    .char_width = 20,\n    .char_height = 10,\n    .font_width = 24,\n    .font_height = 36,\n    .x_offset = 960,\n    .y_offset = 450,\n    .font_page_1 = NULL,\n    .font_page_2 = NULL,\n};\n\nstatic display_info_t *current_display_info;\n\nstatic void sig_handler(int _)\n{\n    quit = 1;\n}\n\nstatic void draw_character(display_info_t *display_info, uint16_t character_map[MAX_DISPLAY_X][MAX_DISPLAY_Y], uint32_t x, uint32_t y, uint16_t c)\n{\n    if ((x > (display_info->char_width - 1)) || (y > (display_info->char_height - 1))) {\n        return;\n    }\n    character_map[x][y] = c;\n}\n\nstatic void msp_draw_character(uint32_t x, uint32_t y, uint16_t c) {\n    draw_character(current_display_info, msp_character_map, x, y, c);\n}\n\nstatic void draw_character_map(display_info_t *display_info, void *fb_addr, uint16_t character_map[MAX_DISPLAY_X][MAX_DISPLAY_Y]) {\n    if (display_info->font_page_1 == NULL) {\n        // give up if we don't have a font loaded\n        return;\n    }\n    void *font;\n    for(int y = 0; y < display_info->char_height; y++) {\n        for(int x = 0; x < display_info->char_width; x++) {\n            uint16_t c = character_map[x][y];\n            if (c != 0) {\n                font = display_info->font_page_1;\n                if (c > 255) {\n                    c = c & 0xFF;\n                    if (display_info->font_page_2 != NULL) {\n                        // fall back to writing page 1 chars if we don't have a page 2 font\n                        font = display_info->font_page_2;\n                    }\n                }\n                uint32_t pixel_x = (x * display_info->font_width) + display_info->x_offset;\n                uint32_t pixel_y = (y * display_info->font_height) + display_info->y_offset;\n                uint32_t character_offset = (((display_info->font_height * display_info->font_width) * BYTES_PER_PIXEL) * c);\n                for(uint8_t gx = 0; gx < display_info->font_width; gx++) {\n                    for(uint8_t gy = 0; gy < display_info->font_height; gy++) {\n                        uint32_t font_offset = character_offset + (gy * display_info->font_width * BYTES_PER_PIXEL) + (gx * BYTES_PER_PIXEL);\n                        uint32_t target_offset = ((((pixel_x + gx) * BYTES_PER_PIXEL) + ((pixel_y + gy) * WIDTH * BYTES_PER_PIXEL)));\n                        *((uint8_t *)fb_addr + target_offset) = *(uint8_t *)((uint8_t *)font + font_offset + 2);\n                        *((uint8_t *)fb_addr + target_offset + 1) = *(uint8_t *)((uint8_t *)font + font_offset + 1);\n                        *((uint8_t *)fb_addr + target_offset + 2) = *(uint8_t *)((uint8_t *)font + font_offset);\n                        *((uint8_t *)fb_addr + target_offset + 3) = ~*(uint8_t *)((uint8_t *)font + font_offset + 3);\n                    }\n                }\n                DEBUG_PRINT(\"%c\", c > 31 ? c : 20);\n            }\n            DEBUG_PRINT(\" \");\n        }\n        DEBUG_PRINT(\"\\n\");\n    }\n}\n\nstatic void draw_screen() {\n    void *fb_addr = dji_display_get_fb_address(dji_display, which_fb);\n    // DJI has a backwards alpha channel - FF is transparent, 00 is opaque.\n    memset(fb_addr, 0x000000FF, WIDTH * HEIGHT * BYTES_PER_PIXEL);\n\n    draw_character_map(current_display_info, fb_addr, msp_character_map);\n    draw_character_map(&overlay_display_info, fb_addr, overlay_character_map);\n}\n\nstatic void msp_clear_screen()\n{\n    memset(msp_character_map, 0, sizeof(msp_character_map));\n}\n\nstatic void msp_draw_complete() {\n    draw_screen();\n    dji_display_push_frame(dji_display, which_fb);\n    which_fb = !which_fb;\n    DEBUG_PRINT(\"drew a frame\\n\");\n}\n\nstatic void msp_callback(msp_msg_t *msp_message)\n{\n    displayport_process_message(display_driver, msp_message);\n}\n\nstatic void get_font_path_with_prefix(char *font_path_dest, const char *font_path, uint8_t len, uint8_t is_hd, uint8_t page) {\n    char name_buf[len];\n    if (is_hd) {\n        snprintf(name_buf, len, \"%s_hd\", font_path);\n    } else {\n        snprintf(name_buf, len, \"%s\", font_path);\n    }\n    if (page > 0) {\n        snprintf(font_path_dest, len, \"%s_%d.bin\", name_buf, page + 1);\n    } else {\n        snprintf(font_path_dest, len, \"%s.bin\", name_buf);\n    }\n}\n\nstatic int open_font(const char *filename, void** font, uint8_t page, uint8_t is_hd) {\n    char file_path[255];\n    get_font_path_with_prefix(file_path, filename, 255, is_hd, page);\n    printf(\"Opening font: %s\\n\", file_path);\n    struct stat st;\n    memset(&st, 0, sizeof(st));\n    stat(file_path, &st);\n    size_t filesize = st.st_size;\n    display_info_t display_info = is_hd ? hd_display_info : sd_display_info;\n    size_t desired_filesize = display_info.font_height *  display_info.font_width * NUM_CHARS * BYTES_PER_PIXEL;\n    if(filesize != desired_filesize) {\n        if (filesize != 0) {\n            printf(\"Font was wrong size: %s %d != %d\\n\", file_path, filesize, desired_filesize);\n        }\n        return -1;\n    }\n    int fd = open(file_path, O_RDONLY, 0);\n    if (!fd) {\n        printf(\"Could not open file %s\\n\", file_path);\n        return -1;\n    }\n    void* mmappedData = mmap(NULL, filesize, PROT_READ, MAP_PRIVATE, fd, 0);\n    // there is no need to keep an FD open after mmap\n    close(fd);\n    if (mmappedData != MAP_FAILED) {\n        *font = mmappedData;\n    } else {\n        printf(\"Could not map font %s\\n\", file_path);\n        *font = 0;\n    }\n    return 0;\n}\n\nstatic void load_font() {\n    if (open_font(SDCARD_FONT_PATH, &sd_display_info.font_page_1, 0, 0) < 0) {\n        if (open_font(ENTWARE_FONT_PATH, &sd_display_info.font_page_1, 0, 0) < 0) {\n          open_font(FALLBACK_FONT_PATH, &sd_display_info.font_page_1, 0, 0);\n        }\n    }\n    if (open_font(SDCARD_FONT_PATH, &sd_display_info.font_page_2, 1, 0) < 0) {\n        if (open_font(ENTWARE_FONT_PATH, &sd_display_info.font_page_2, 1, 0) < 0) {\n          open_font(FALLBACK_FONT_PATH, &sd_display_info.font_page_2, 1, 0);\n        }\n    }\n    if (open_font(SDCARD_FONT_PATH, &hd_display_info.font_page_1, 0, 1) < 0) {\n        if (open_font(ENTWARE_FONT_PATH, &hd_display_info.font_page_1, 0, 1) < 0) {\n          open_font(FALLBACK_FONT_PATH, &hd_display_info.font_page_1, 0, 1);\n        }\n    }\n    if (open_font(SDCARD_FONT_PATH, &hd_display_info.font_page_2, 1, 1) < 0) {\n        if (open_font(ENTWARE_FONT_PATH, &hd_display_info.font_page_2, 1, 1) < 0) {\n          open_font(FALLBACK_FONT_PATH, &hd_display_info.font_page_2, 1, 1);\n        }\n    }\n    if (open_font(SDCARD_FONT_PATH, &overlay_display_info.font_page_1, 0, 1) < 0) {\n        if (open_font(ENTWARE_FONT_PATH, &overlay_display_info.font_page_1, 0, 1) < 0) {\n          open_font(FALLBACK_FONT_PATH, &overlay_display_info.font_page_1, 0, 1);\n        }\n    }\n    if (open_font(SDCARD_FONT_PATH, &overlay_display_info.font_page_2, 1, 1) < 0) {\n        if (open_font(ENTWARE_FONT_PATH, &overlay_display_info.font_page_2, 1, 1) < 0) {\n          open_font(FALLBACK_FONT_PATH, &overlay_display_info.font_page_2, 1, 1);\n        }\n    }\n}\n\nstatic void close_fonts(display_info_t *display_info) {\n    if (display_info->font_page_1 != NULL)\n    {\n        munmap(display_info->font_page_1, display_info->font_height * display_info->font_width * NUM_CHARS * 4);\n    }\n    if (display_info->font_page_2 != NULL)\n    {\n        munmap(display_info->font_page_2, display_info->font_height * display_info->font_width * NUM_CHARS * 4);\n    }\n}\n\nstatic void msp_set_options(uint8_t font_num, uint8_t is_hd) {\n    msp_clear_screen();\n    if(is_hd) {\n        current_display_info = &hd_display_info;\n    } else {\n        current_display_info = &sd_display_info;\n    }\n}\n\nstatic void display_print_string(uint8_t init_x, uint8_t y, const char *string, uint8_t len) {\n    for(uint8_t x = 0; x < len; x++) {\n        draw_character(&overlay_display_info, overlay_character_map, x + init_x, y, string[x]);\n    }\n}\n\nstatic void start_display(uint8_t is_v2_goggles) {\n    memset(msp_character_map, 0, sizeof(msp_character_map));\n    memset(overlay_character_map, 0, sizeof(overlay_character_map));\n\n    dji_display = dji_display_state_alloc(is_v2_goggles);\n    dji_display_open_framebuffer(dji_display, PLANE_ID);\n    display_print_string(0, overlay_display_info.char_height -1, SPLASH_STRING, sizeof(SPLASH_STRING));\n    msp_draw_complete();\n}\n\nstatic void stop_display() {\n    display_print_string(0, overlay_display_info.char_height -1, SHUTDOWN_STRING, sizeof(SHUTDOWN_STRING));\n    dji_display_close_framebuffer(dji_display);\n    dji_display_state_free(dji_display);\n}\n\nstatic void process_data_packet(uint8_t *buf, int len, dji_shm_state_t *radio_shm) {\n    packet_data_t *packet = (packet_data_t *)buf;\n    DEBUG_PRINT(\"got data %f mbit %d C %f V\\n\", packet->tx_bitrate / 1000.0f, packet->tx_temperature, packet->tx_voltage / 64.0f);\n    memset(overlay_character_map, 0, sizeof(overlay_character_map));\n    char str[8];\n    snprintf(str, 8, \"%2.1fMB \", packet->tx_bitrate / 1000.0f);\n    display_print_string(overlay_display_info.char_width - 6, overlay_display_info.char_height - 5, str, 6);\n    uint16_t latency = dji_radio_latency_ms(radio_shm);\n    snprintf(str, 8, \"%d MS\", latency);\n    display_print_string(overlay_display_info.char_width - 6, overlay_display_info.char_height - 4, str, 6);\n    snprintf(str, 8, \"%d C\", packet->tx_temperature);\n    display_print_string(overlay_display_info.char_width - 5, overlay_display_info.char_height - 3, str, 5);\n    snprintf(str, 8, \"A %2.1fV\", packet->tx_voltage / 64.0f);\n    display_print_string(overlay_display_info.char_width - 7, overlay_display_info.char_height - 2, str, 7);\n    uint16_t goggle_voltage = get_int_from_fs(GOGGLES_VOLTAGE_PATH);\n    snprintf(str, 8, \"G %2.1fV\", (goggle_voltage / 45.0f) - 0.65f);\n    display_print_string(overlay_display_info.char_width - 7, overlay_display_info.char_height - 1, str, 7);\n}\n\nint main(int argc, char *argv[])\n{\n    signal(SIGINT, sig_handler);\n\n    current_display_info = &sd_display_info;\n\n    uint8_t is_v2_goggles = dji_goggles_are_v2();\n    printf(\"Detected DJI goggles %s\\n\", is_v2_goggles ? \"V2\" : \"V1\");\n\n    display_driver = calloc(1, sizeof(displayport_vtable_t));\n    display_driver->draw_character = &msp_draw_character;\n    display_driver->clear_screen = &msp_clear_screen;\n    display_driver->draw_complete = &msp_draw_complete;\n    display_driver->set_options = &msp_set_options;\n\n    msp_state_t *msp_state = calloc(1, sizeof(msp_state_t));\n    msp_state->cb = &msp_callback;\n\n    int event_fd = open(INPUT_FILENAME, O_RDONLY);\n    assert(event_fd > 0);\n\n    dji_shm_state_t radio_shm;\n    memset(&radio_shm, 0, sizeof(radio_shm));\n\n    int msp_socket_fd = bind_socket(MSP_PORT);\n    int data_socket_fd = bind_socket(DATA_PORT);\n    printf(\"started up, listening on port %d\\n\", MSP_PORT);\n\n\n    struct pollfd poll_fds[3];\n    int recv_len = 0;\n    uint8_t byte = 0;\n    uint8_t buffer[4096];\n    struct sockaddr_storage src_addr;\n    socklen_t src_addr_len=sizeof(src_addr);\n    struct input_event ev;\n    struct timespec button_start, display_start, now;\n    memset(&display_start, 0, sizeof(display_start));\n    memset(&button_start, 0, sizeof(button_start));\n\n    enum display_mode_s {\n        DISPLAY_DISABLED = 0,\n        DISPLAY_RUNNING = 1,\n        DISPLAY_WAITING = 2\n    } display_mode = DISPLAY_DISABLED;\n\n    while (!quit)\n    {\n        clock_gettime(CLOCK_MONOTONIC, &now);\n        if(display_mode == DISPLAY_WAITING && display_start.tv_sec > 0 && ((now.tv_sec - display_start.tv_sec) > 1)) {\n            // Wait 1 second between stopping Glasses service and trying to start OSD.\n            memset(&display_start, 0, sizeof(display_start));\n            load_font();\n            open_dji_radio_shm(&radio_shm);\n            start_display(is_v2_goggles);\n            display_mode = DISPLAY_RUNNING;\n        }\n        if(button_start.tv_sec > 0 && ((now.tv_sec - button_start.tv_sec) > BACK_BUTTON_DELAY)) {\n            // We held the back button down for 5 seconds.\n            memset(&button_start, 0, sizeof(button_start));\n            if (display_mode == DISPLAY_DISABLED) {\n                printf(\"Switching Disabled -> Enabled!\\n\");\n                dji_stop_goggles(is_v2_goggles);\n                clock_gettime(CLOCK_MONOTONIC, &display_start);\n                display_mode = DISPLAY_WAITING;\n            } else {\n                printf(\"Switching Enabled/Waiting -> Disabled!\\n\");\n                if(display_mode == DISPLAY_RUNNING) {\n                    stop_display();\n                    close_dji_radio_shm(&radio_shm);\n                }\n                close_fonts(&sd_display_info);\n                close_fonts(&hd_display_info);\n                close_fonts(&overlay_display_info);\n                display_mode = DISPLAY_DISABLED;\n                dji_start_goggles(is_v2_goggles);\n            }\n        }\n\n        poll_fds[0].fd = msp_socket_fd;\n        poll_fds[0].events = POLLIN;\n        poll_fds[1].fd = event_fd;\n        poll_fds[1].events = POLLIN;\n        poll_fds[2].fd = data_socket_fd;\n        poll_fds[2].events = POLLIN;\n        poll(poll_fds, 3, 100);\n\n        if(poll_fds[1].revents) {\n            read(event_fd, &ev, sizeof(struct input_event));\n            if(ev.code == EV_CODE_BACK) {\n                if(ev.value == 1) {\n                    clock_gettime(CLOCK_MONOTONIC, &button_start);\n                } else {\n                    memset(&button_start, 0, sizeof(button_start));\n                }\n            }\n            DEBUG_PRINT(\"input type: %i, code: %i, value: %i\\n\", ev.type, ev.code, ev.value);\n        }\n        if(poll_fds[0].revents) {\n            // Got MSP UDP packet\n            if (0 < (recv_len = recvfrom(msp_socket_fd,&buffer,sizeof(buffer),0,(struct sockaddr*)&src_addr,&src_addr_len)))\n            {\n                DEBUG_PRINT(\"got MSP packet len %d\\n\", recv_len);\n                if(display_mode == DISPLAY_RUNNING) {\n                    for (int i=0; i<recv_len; i++)\n                        msp_process_data(msp_state, buffer[i]);\n                }\n            }\n        }\n         if(poll_fds[2].revents) {\n            // Got data UDP packet\n            if (0 < (recv_len = recvfrom(data_socket_fd,&buffer,sizeof(buffer),0,(struct sockaddr*)&src_addr,&src_addr_len)))\n            {\n                DEBUG_PRINT(\"got DATA packet len %d\\n\", recv_len);\n                if(display_mode == DISPLAY_RUNNING) {\n                    process_data_packet(buffer, recv_len, &radio_shm);\n                }\n            }\n        }\n    }\n    if(display_mode == DISPLAY_RUNNING) {\n        stop_display();\n    }\n\n    free(display_driver);\n    free(msp_state);\n    close(msp_socket_fd);\n    close(data_socket_fd);\n    close(event_fd);\n    return 0;\n}\n"
  },
  {
    "path": "jni/osd_sfml_udp.c",
    "content": "#include <stdio.h>\n#include <arpa/inet.h>\n#include <sys/socket.h>\n#include <netinet/in.h>\n#include <unistd.h>\n#include <fcntl.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/poll.h>\n#include <time.h>\n\n#include <SFML/Graphics.h>\n#include <SFML/Window.h>\n\n#include \"msp/msp.h\"\n#include \"msp/msp_displayport.h\"\n#include \"net/serial.h\"\n#include \"net/network.h\"\n#include \"util/debug.h\"\n\n#define X_OFFSET 120\n\n#define PORT 7654\n\n#define WIDTH 1440\n#define HEIGHT 810\n\ntypedef struct display_info_s {\n    uint8_t char_width;\n    uint8_t char_height;\n    uint8_t font_width;\n    uint8_t font_height;\n    uint16_t num_chars;\n} display_info_t; \n\n#define SD_DISPLAY_INFO {.char_width = 31, .char_height = 15, .font_width = 36, .font_height = 54, .num_chars = 256}\n\nstatic const display_info_t sd_display_info = SD_DISPLAY_INFO;\n\nstatic const display_info_t hd_display_info = {\n    .char_width = 50,\n    .char_height = 18,\n    .font_width = 24,\n    .font_height = 36,\n    .num_chars = 512,\n};\n\n#define MAX_OSD_WIDTH 50\n#define MAX_OSD_HEIGHT 18\n\nstatic display_info_t current_display_info = SD_DISPLAY_INFO;\n\nstatic volatile sig_atomic_t quit = 0;\nsfTexture *font_1;\nsfTexture *font_2;\nsfSprite *font_sprite_1;\nsfSprite *font_sprite_2;\nsfRenderWindow *window;\nuint16_t character_map[MAX_OSD_WIDTH][MAX_OSD_HEIGHT];\ndisplayport_vtable_t *display_driver;\n\nstatic void sig_handler(int _)\n{\n    quit = 1;\n}\n\nstatic void draw_character(uint32_t x, uint32_t y, uint16_t c)\n{\n    if (x > current_display_info.char_width - 1 || y > current_display_info.char_height - 1)\n    {\n        return;\n    }\n    character_map[x][y] = c;\n}\n\nstatic void draw_screen()\n{\n    sfRenderWindow_clear(window, sfColor_fromRGB(55, 55, 55));\n    for (int y = 0; y < current_display_info.char_height; y++)\n    {\n        for (int x = 0; x < current_display_info.char_width; x++)\n        {\n            uint16_t c = character_map[x][y];\n            if (c != 0)\n            {\n                uint8_t page = 0;\n                if (c > 255) {\n                    page = 1;\n                    c = c & 0xFF;\n                }\n                DEBUG_PRINT(\"%02X\", c);\n                sfIntRect r = {0, current_display_info.font_height * c, current_display_info.font_width, current_display_info.font_height};\n                sfVector2f dest = {(x * current_display_info.font_width) + X_OFFSET, y * current_display_info.font_height};\n                sfSprite *font_sprite = page ? font_sprite_2 : font_sprite_1;\n                sfSprite_setTextureRect(font_sprite, r);\n                sfSprite_setPosition(font_sprite, dest);\n                sfRenderWindow_drawSprite(window, font_sprite, NULL);\n            }\n            DEBUG_PRINT(\"  \");\n        }\n        DEBUG_PRINT(\"\\n\");\n    }\n}\n\nstatic void clear_screen()\n{\n    DEBUG_PRINT(\"clear\\n\");\n    memset(character_map, 0, sizeof(character_map));\n}\n\nstatic void draw_complete()\n{\n    draw_screen();\n    sfRenderWindow_display(window);\n    DEBUG_PRINT(\"draw complete!\\n\");\n}\n\nstatic void msp_callback(msp_msg_t *msp_message)\n{\n    displayport_process_message(display_driver, msp_message);\n}\n\nstatic void set_options(uint8_t font, uint8_t is_hd) {\n    if(is_hd) { \n        current_display_info = hd_display_info;\n    } else {\n        current_display_info = sd_display_info;\n    }\n}\n\nint main(int argc, char *argv[])\n{\n    struct pollfd poll_fds[1];\n    signal(SIGINT, sig_handler);\n    memset(character_map, 0, sizeof(character_map));\n    sfVideoMode videoMode = {1440, 810, 32};\n    window = sfRenderWindow_create(videoMode, \"MSP OSD\", 0, NULL);\n    sfRenderWindow_display(window);\n    char *font_name;\n    if (argc > 1) {\n        font_name = argv[1];\n    } else {\n        font_name = \"bold.png\";\n    }\n    char font_load_name[255];\n    snprintf(font_load_name, 255, \"%s.png\", font_name);\n    font_1 = sfTexture_createFromFile(font_name, NULL);\n    font_sprite_1 = sfSprite_create();\n    sfSprite_setTexture(font_sprite_1, font_1, 0);\n    snprintf(font_2_name, 255, \"%s_2.png\", font_name);\n    font_2 = sfTexture_createFromFile(font_2_name, NULL);\n    font_sprite_2 = sfSprite_create();\n    sfSprite_setTexture(font_sprite_2, font_2, 0);\n\n    display_driver = calloc(1, sizeof(displayport_vtable_t));\n    display_driver->draw_character = &draw_character;\n    display_driver->clear_screen = &clear_screen;\n    display_driver->draw_complete = &draw_complete;\n    display_driver->set_options = &set_options;\n\n    msp_state_t *msp_state = calloc(1, sizeof(msp_state_t));\n    msp_state->cb = &msp_callback;\n\n    int socket_fd = bind_socket(PORT);\n    int recv_len = 0;\n    uint8_t buffer[4096];\n\n    struct timespec fps_start, now;\n    uint32_t message_counter = 0;\n    clock_gettime(CLOCK_MONOTONIC, &fps_start);\n\n    printf(\"started up, listening on port %d\\n\", PORT);\n\n    while (!quit)\n    {\n        clock_gettime(CLOCK_MONOTONIC, &now);\n        if(now.tv_sec > fps_start.tv_sec) {\n            clock_gettime(CLOCK_MONOTONIC, &fps_start);\n            printf(\"Got %d messages in the last second\\n\", message_counter);\n            message_counter = 0;\n        }        \n\n        sfEvent event;\n        sfRenderWindow_pollEvent(window, &event);\n\n        // Close window: exit\n        if (event.type == sfEvtMouseButtonReleased)\n        {\n            sfRenderWindow_close(window);\n            quit = 1;\n        }\n\n        poll_fds[0].fd = socket_fd;\n        poll_fds[0].events = POLLIN;\n        if(poll(poll_fds, 1, 50) > 0) // poll every 50ms so we also go back through the SFML loop  \n        {\n            struct sockaddr_storage src_addr;\n            socklen_t src_addr_len=sizeof(src_addr);\n            if (0 < (recv_len = recvfrom(socket_fd,&buffer,sizeof(buffer),0,(struct sockaddr*)&src_addr,&src_addr_len)))\n            {\n                message_counter++;\n                for (int i=0; i<recv_len; i++) \n                    msp_process_data(msp_state, buffer[i]);\n            }\n        }\n    }\n    sfRenderWindow_close(window);\n    sfSprite_destroy(font_sprite_1);\n    sfSprite_destroy(font_sprite_2);\n    sfTexture_destroy(font_1);\n    sfTexture_destroy(font_2);\n    sfRenderWindow_destroy(window);\n    free(msp_state);\n    free(display_driver);\n    return 0;\n}"
  },
  {
    "path": "jni/rec/rec.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n#include \"../json/osd_config.h\"\n\n#include \"rec.h\"\n#include \"rec_shim.h\"\n#include \"rec_util.h\"\n\n#define REC_CONFIG_ENABLED_KEY \"rec_enabled\"\n\n#ifdef DEBUG\n#define DEBUG_PRINT(fmt, args...) fprintf(stderr, \"msp_osd.rec: \" fmt \"\\n\", ##args)\n#else\n#define DEBUG_PRINT(fmt, args...)\n#endif\n\nstatic bool rec_is_ready();\nstatic uint32_t rec_get_frame_idx();\n\nstatic bool rec_enabled = false;\ngs_lv_transcode_t *rec_lv_transcode = NULL;\nstatic FILE *rec_fd = NULL;\nstatic bool rec_recording = false;\n\nvoid rec_start(rec_config_t *config)\n{\n    if (!rec_is_ready())\n        return;\n\n    if (rec_fd != NULL)\n    {\n        fflush(rec_fd);\n        fclose(rec_fd);\n    }\n\n    char rec_file_name[256];\n    rec_util_osd_path_from_video_path(\n        rec_lv_transcode->file_name,\n        rec_file_name,\n        sizeof(rec_file_name));\n    DEBUG_PRINT(\"rec_file_name: %s\", rec_file_name);\n\n    DEBUG_PRINT(\"Config:\\n\");\n    DEBUG_PRINT(\"  Char Width: %u\\n\", config->char_width);\n    DEBUG_PRINT(\"  Char Height: %u\\n\", config->char_height);\n    DEBUG_PRINT(\"  Font Width: %u\\n\", config->font_width);\n    DEBUG_PRINT(\"  Font Height: %u\\n\", config->font_height);\n    DEBUG_PRINT(\"  X Offset: %u\\n\", config->x_offset);\n    DEBUG_PRINT(\"  Y Offset: %u\\n\", config->y_offset);\n    DEBUG_PRINT(\"  Font Variant: %.5s\\n\", config->font_variant);\n\n    rec_fd = fopen(rec_file_name, \"wb\");\n    if (rec_fd == NULL)\n    {\n        DEBUG_PRINT(\"Failed to open file: %s\", rec_file_name);\n        return;\n    }\n\n    rec_file_header_t file_header = {\n        .magic = REC_MAGIC,\n        .version = REC_VERSION,\n    };\n    memcpy(&file_header.config, config, sizeof(rec_config_t));\n    fwrite(&file_header, sizeof(rec_file_header_t), 1, rec_fd);\n\n    rec_recording = true;\n}\n\nvoid rec_stop()\n{\n    if (rec_fd != NULL)\n    {\n        fflush(rec_fd);\n        fclose(rec_fd);\n        rec_fd = NULL;\n    }\n\n    rec_recording = false;\n}\n\nvoid rec_write_frame(uint16_t *frame_data, size_t frame_size)\n{\n    if (rec_fd == NULL)\n        return;\n\n    rec_frame_header_t frame_header = {\n        .frame_idx = rec_get_frame_idx(),\n        .size = frame_size,\n    };\n\n    fwrite(&frame_header, sizeof(frame_header), 1, rec_fd);\n    fwrite(frame_data, sizeof(uint16_t), frame_size, rec_fd);\n}\n\nvoid rec_load_config()\n{\n    rec_enabled = get_boolean_config_value(REC_CONFIG_ENABLED_KEY);\n\n    DEBUG_PRINT(\"rec_enabled: %d\", rec_enabled);\n}\n\nbool rec_is_enabled()\n{\n    return rec_enabled;\n}\n\nbool rec_is_osd_recording()\n{\n    return rec_recording;\n}\n\nbool rec_is_gls_recording()\n{\n    if (rec_is_ready() == false)\n        return false;\n\n    return rec_lv_transcode->cur_state == RECORD_STATE_RECORDING;\n}\n\nstatic bool rec_is_ready()\n{\n    return rec_lv_transcode != NULL;\n}\n\nstatic uint32_t rec_get_frame_idx()\n{\n\n    return rec_lv_transcode->last_frame_idx;\n}\n"
  },
  {
    "path": "jni/rec/rec.h",
    "content": "#pragma once\n\n#include <stdbool.h>\n#include <stdint.h>\n\n#define REC_MAGIC \"MSPOSD\"\n#define REC_VERSION 2\n\ntypedef struct rec_config_t\n{\n    uint8_t char_width;\n    uint8_t char_height;\n    uint8_t font_width;\n    uint8_t font_height;\n    uint16_t x_offset;\n    uint16_t y_offset;\n    char font_variant[5];\n} __attribute__((packed)) rec_config_t;\n\ntypedef struct rec_file_header_t\n{\n    char magic[7];\n    uint16_t version;\n    rec_config_t config;\n} __attribute__((packed)) rec_file_header_t;\n\ntypedef struct rec_config_v1_t\n{\n    uint8_t char_width;\n    uint8_t char_height;\n    uint8_t font_width;\n    uint8_t font_height;\n    uint16_t x_offset;\n    uint16_t y_offset;\n    uint8_t font_variant;\n} __attribute__((packed)) rec_config_v1_t;\n\ntypedef struct rec_file_header_v1_t\n{\n    char magic[7];\n    uint16_t version;\n    rec_config_v1_t config;\n} __attribute__((packed)) rec_file_header_v1_t;\n\ntypedef struct rec_frame_header_t\n{\n    uint32_t frame_idx;\n    uint32_t size;\n} __attribute__((packed)) rec_frame_header_t;\n\nvoid rec_start();\nvoid rec_stop();\nvoid rec_load_config();\nvoid rec_write_frame(uint16_t *frame_data, size_t frame_size);\nbool rec_is_enabled();\nbool rec_is_osd_recording();\nbool rec_is_gls_recording();\n\n"
  },
  {
    "path": "jni/rec/rec_pb.c",
    "content": "#include <stdint.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/ioctl.h>\n\n#include \"../json/osd_config.h\"\n\n#include \"rec.h\"\n#include \"rec_shim.h\"\n#include \"rec_util.h\"\n\n#include \"rec_pb.h\"\n\n#include \"../font/font.h\"\n\n#define REC_PB_CONFIG_ENABLED_KEY \"rec_pb_enabled\"\n\n#define MAX_X 60\n#define MAX_Y 22\n#define MAX_T (MAX_X * MAX_Y)\n\n#define FRAME_SIZE (sizeof(rec_frame_header_t) + (sizeof(uint16_t) * MAX_T))\n\n#ifdef DEBUG\n#define DEBUG_PRINT(fmt, args...) fprintf(stderr, \"msp_osd.rec_pb: \" fmt \"\\n\", ##args)\n#else\n#define DEBUG_PRINT(fmt, args...)\n#endif\n\ncp_vdec_t *rec_pb_cp_vdec = NULL;\nvdec_local_player_t *rec_pb_vdec_local_player = NULL;\nuint8_t rec_pb_start_attempted = false;\n\nstatic bool rec_pb_enabled = false;\n\nstatic FILE *osd_fd = NULL;\nstatic rec_config_t osd_config = {0};\n\nstatic uint32_t header_size = 0;\nstatic int64_t frame_counter = 0;\n\nstatic uint32_t *frame_idxs;\nstatic uint32_t frame_idx_len = 0;\nstatic uint32_t current_frame_idx = 0;\n\nvoid rec_pb_load_config()\n{\n    rec_pb_enabled = get_boolean_config_value(REC_PB_CONFIG_ENABLED_KEY);\n\n    DEBUG_PRINT(\"rec_pb_enabled: %d\", rec_pb_enabled);\n}\n\nbool rec_pb_is_enabled()\n{\n    return rec_pb_enabled;\n}\n\nint rec_pb_start()\n{\n    if (rec_pb_is_ready() == false)\n    {\n        return 1;\n    }\n\n    if (osd_fd != NULL)\n    {\n        return 1;\n    }\n\n    // TODO: Mutex needed here? Can cause a crash if placback stops as we're trying to read info.\n    DEBUG_PRINT(\"playback on: %s\", rec_pb_vdec_local_player->fmt_ctx->filename);\n\n    char osd_path[256];\n    rec_util_osd_path_from_video_path(rec_pb_vdec_local_player->fmt_ctx->filename, osd_path, sizeof(osd_path));\n\n    DEBUG_PRINT(\"osd path: %s\", osd_path);\n    osd_fd = fopen(osd_path, \"rb\");\n    if (osd_fd == NULL)\n    {\n        DEBUG_PRINT(\"osd file not found\");\n        return 1;\n    }\n\n    DEBUG_PRINT(\"osd file found\");\n    rec_file_header_t file_header;\n    fread(&file_header, sizeof(rec_file_header_t), 1, osd_fd);\n\n    if (strncmp(file_header.magic, REC_MAGIC, sizeof(REC_MAGIC)) != 0)\n    {\n        DEBUG_PRINT(\"invalid osd file\");\n        fclose(osd_fd);\n        osd_fd = NULL;\n        return 1;\n    }\n\n    if (file_header.version == REC_VERSION)\n    {\n        DEBUG_PRINT(\"header ok!\");\n        memcpy(&osd_config, &file_header.config, sizeof(rec_config_t));\n        header_size = sizeof(rec_file_header_t);\n    }\n    else if (file_header.version == 1)\n    {\n        DEBUG_PRINT(\"header is v1\");\n        rec_file_header_v1_t file_header_v1;\n        fseek(osd_fd, 0, SEEK_SET);\n\n        fread(&file_header_v1, sizeof(rec_file_header_v1_t), 1, osd_fd);\n        if (strncmp(file_header_v1.magic, REC_MAGIC, sizeof(REC_MAGIC)) != 0)\n        {\n            DEBUG_PRINT(\"invalid osd file\");\n            fclose(osd_fd);\n            osd_fd = NULL;\n            return 1;\n        }\n\n        switch (file_header_v1.config.font_variant)\n        {\n        case FONT_VARIANT_BETAFLIGHT:\n            strcpy(file_header.config.font_variant, \"BTFL\");\n            break;\n        case FONT_VARIANT_INAV:\n            strcpy(file_header.config.font_variant, \"INAV\");\n            break;\n        case FONT_VARIANT_ARDUPILOT:\n            strcpy(file_header.config.font_variant, \"ARDU\");\n            break;\n        case FONT_VARIANT_KISS_ULTRA:\n            strcpy(file_header.config.font_variant, \"ULTR\");\n            break;\n        case FONT_VARIANT_QUICKSILVER:\n            strcpy(file_header.config.font_variant, \"QUIC\");\n            break;\n        default:\n            file_header.config.font_variant[0] = '\\0'; // Empty string\n        }\n\n        memcpy(&osd_config, &file_header.config, sizeof(rec_config_t));\n        header_size = sizeof(rec_file_header_v1_t);\n    }\n    else\n    {\n        DEBUG_PRINT(\"invalid osd file version! expected: %d, got: %d\", REC_VERSION, file_header.version);\n        fclose(osd_fd);\n        osd_fd = NULL;\n        return 1;\n    }\n\n    DEBUG_PRINT(\"loading frame indexes\");\n\n    fseek(osd_fd, 0, SEEK_END);\n    uint32_t file_size = ftell(osd_fd);\n    fseek(osd_fd, header_size, SEEK_SET);\n    DEBUG_PRINT(\"file size: %d\", file_size);\n\n    frame_idx_len = file_size / FRAME_SIZE;\n    DEBUG_PRINT(\"frame_idx_len: %d\", frame_idx_len);\n    frame_idxs = malloc(sizeof(uint32_t) * frame_idx_len);\n\n    for (uint32_t i = 0; i < frame_idx_len; i++)\n    {\n        rec_frame_header_t frame_header;\n        fread(&frame_header, sizeof(rec_frame_header_t), 1, osd_fd);\n        frame_idxs[i] = frame_header.frame_idx;\n        DEBUG_PRINT(\"frame_idx: %d = %d\", i, frame_idxs[i]);\n        fseek(osd_fd, sizeof(uint16_t) * MAX_T, SEEK_CUR);\n    }\n\n    fseek(osd_fd, header_size, SEEK_SET);\n\n    current_frame_idx = 0;\n    frame_counter = rec_pb_cp_vdec->frames_sent;\n\n    return 0;\n}\n\nvoid rec_pb_stop()\n{\n    if (osd_fd != NULL)\n    {\n        fclose(osd_fd);\n        osd_fd = NULL;\n    }\n\n    rec_pb_cp_vdec = NULL;\n    rec_pb_vdec_local_player = NULL;\n\n    free(frame_idxs);\n    frame_idxs = NULL;\n\n    DEBUG_PRINT(\"playback stopped\");\n}\n\nrec_config_t *rec_pb_get_config()\n{\n    if (rec_pb_is_ready() == false)\n    {\n        return NULL;\n    }\n\n    return &osd_config;\n}\n\nint rec_pb_do_next_frame(uint16_t *map_out)\n{\n    // -45 is absolutely a magic number based on testing, seems play_tm_ms lags by about that much.\n    uint64_t frame_counter = ((rec_pb_cp_vdec->play_tm_ms) * 60 / 1000) - 45;\n\n    uint32_t closest_frame_idx = 0;\n    for (uint32_t i = 0; i < frame_idx_len; i++)\n    {\n        if (frame_idxs[i] > frame_counter)\n        {\n            break;\n        }\n\n        closest_frame_idx = i;\n    }\n\n    if (closest_frame_idx == current_frame_idx && current_frame_idx != 0)\n    {\n        return 0;\n    }\n\n    fseek(\n        osd_fd,\n        header_size + (closest_frame_idx * FRAME_SIZE) + sizeof(rec_frame_header_t),\n        SEEK_SET);\n    fread(map_out, sizeof(uint16_t), MAX_T, osd_fd);\n\n    current_frame_idx = closest_frame_idx;\n\n    return 0;\n}\n\nbool rec_pb_gls_is_playing()\n{\n    if (rec_pb_is_ready() == false)\n    {\n        return false;\n    }\n\n    // state == 5 is stopped (i.e., when gs_player_stop is called)\n    return rec_pb_vdec_local_player->b_running && !rec_pb_vdec_local_player->b_v_eos && rec_pb_vdec_local_player->state != 5;\n}\n\nbool rec_pb_is_ready()\n{\n    return rec_pb_cp_vdec != NULL && rec_pb_vdec_local_player != NULL;\n}\n\n"
  },
  {
    "path": "jni/rec/rec_pb.h",
    "content": "#pragma once\n\n#include <stdlib.h>\n#include <stdint.h>\n#include <stdbool.h>\n\nextern uint8_t rec_pb_start_attempted;\n\nvoid rec_pb_load_config();\nbool rec_pb_is_enabled();\n\nint rec_pb_start();\nvoid rec_pb_stop();\n\nint rec_pb_do_next_frame(uint16_t *map_out);\n\nrec_config_t *rec_pb_get_config();\n\nbool rec_pb_is_ready();\nbool rec_pb_gls_is_playing();\n"
  },
  {
    "path": "jni/rec/rec_shim.c",
    "content": "#include <dlfcn.h>\n#include <stdio.h>\n#include <string.h>\n\n#include \"../hw/dji_services.h\"\n#include \"rec_shim.h\"\n\n#ifdef DEBUG\n#define DEBUG_PRINT(fmt, args...) fprintf(stderr, \"msp_osd.rec_shim: \" fmt \"\\n\", ##args)\n#else\n#define DEBUG_PRINT(fmt, args...)\n#endif\n\nduss_osal_priority_t (*duss_osal_task_create_orig)(\n    duss_osal_task_attrib_t *task_attrib,\n    duss_osal_task_handle_t **task_handle) = NULL;\n\nextern cp_vdec_t *rec_pb_cp_vdec;\nextern vdec_local_player_t *rec_pb_vdec_local_player;\n\nextern gs_lv_transcode_t *rec_lv_transcode;\n\nduss_osal_priority_t duss_osal_task_create(\n    duss_osal_task_attrib_t *task_attrib,\n    duss_osal_task_handle_t **task_handle)\n{\n    if (duss_osal_task_create_orig == NULL)\n    {\n        duss_osal_task_create_orig = dlsym(RTLD_NEXT, \"duss_osal_task_create\");\n    }\n\n    if (strcmp(task_attrib->name, \"record_thread\") == 0)\n    {\n        rec_lv_transcode = task_attrib->param;\n        DEBUG_PRINT(\"got lv_transcode_t from record_thread: %p\", rec_lv_transcode);\n    }\n    else if (strcmp(task_attrib->name, \"player_thread\") == 0)\n    {\n        gs_info_t *ctx = task_attrib->param;\n\n        if (dji_goggles_are_v2())\n        {\n            rec_pb_vdec_local_player = *(vdec_local_player_t **)((void *)ctx + 0x8b8);\n        }\n        else\n        {\n            rec_pb_vdec_local_player = *(vdec_local_player_t **)((void *)ctx + 0x8b4);\n        }\n\n        DEBUG_PRINT(\"got vdec_local_player_t from player_thread: %p\", rec_pb_vdec_local_player);\n    }\n    else if (strcmp(task_attrib->name, \"vdec_thread\") == 0)\n    {\n        rec_pb_cp_vdec = task_attrib->param;\n        DEBUG_PRINT(\"got cp_vdec_t from vdec_thread: %p\", rec_pb_cp_vdec);\n    }\n\n    return duss_osal_task_create_orig(task_attrib, task_handle);\n}\n"
  },
  {
    "path": "jni/rec/rec_shim.h",
    "content": "#pragma once\n\n#include <stdbool.h>\n\ntypedef unsigned char undefined;\n\ntypedef unsigned char byte;\ntypedef unsigned int dword;\ntypedef long long longlong;\ntypedef unsigned long long qword;\ntypedef unsigned char uchar;\ntypedef unsigned int uint;\ntypedef unsigned long ulong;\ntypedef unsigned long long ulonglong;\ntypedef unsigned char undefined1;\ntypedef unsigned short undefined2;\ntypedef unsigned int undefined4;\ntypedef unsigned long long undefined8;\ntypedef unsigned short ushort;\ntypedef unsigned short word;\ntypedef struct __gs_info __gs_info;\n\ntypedef struct __gs_info gs_info_t;\n\ntypedef struct duss_event_client duss_event_client;\n\ntypedef struct duss_event_client *duss_event_client_handle_t;\n\ntypedef struct duss_hal_obj duss_hal_obj;\n\ntypedef struct duss_hal_obj *duss_hal_obj_handle_t;\n\ntypedef int __int32_t;\n\ntypedef __int32_t int32_t;\n\ntypedef struct gs_debug_ctrl gs_debug_ctrl;\n\ntypedef struct gs_debug_ctrl gs_debug_ctrl_t;\n\ntypedef struct duss_osal_task_handle_t duss_osal_task_handle_t;\n\ntypedef struct __gs_local_sd_info __gs_local_sd_info;\n\ntypedef struct __gs_local_sd_info gs_local_sd_info_t;\n\ntypedef struct duss_osal_timer_handle_t duss_osal_timer_handle_t;\n\ntypedef struct _DUSS_MSG_RC_BAT_INFO_t _DUSS_MSG_RC_BAT_INFO_t;\n\ntypedef struct _DUSS_MSG_RC_BAT_INFO_t DUSS_MSG_RC_BAT_INFO_t;\n\ntypedef struct gs_battery_info gs_battery_info;\n\ntypedef struct gs_battery_info gs_battery_info_t;\n\ntypedef uchar __uint8_t;\n\ntypedef __uint8_t uint8_t;\n\ntypedef struct gs_modem_ctrl gs_modem_ctrl;\n\ntypedef struct gs_modem_ctrl gs_modem_ctrl_t;\n\ntypedef struct _DUSS_MSG_RC_MS_LINK_STATUS _DUSS_MSG_RC_MS_LINK_STATUS;\n\ntypedef struct _DUSS_MSG_RC_MS_LINK_STATUS DUSS_MSG_RC_MS_LINK_STATUS_t;\n\ntypedef struct __gs_use_times_info __gs_use_times_info;\n\ntypedef struct __gs_use_times_info gs_use_times_info_t;\n\ntypedef struct gs_rc_ctrl gs_rc_ctrl;\n\ntypedef struct gs_rc_ctrl gs_rc_ctrl_t;\n\ntypedef struct keys_pack_to_racing_glass_t keys_pack_to_racing_glass_t;\n\ntypedef struct duss_osal_timer_attrib_t duss_osal_timer_attrib_t;\n\ntypedef struct pack_for_factory_test_t pack_for_factory_test_t;\n\ntypedef struct gs_buzzer_info gs_buzzer_info;\n\ntypedef struct gs_buzzer_info gs_buzzer_info_t;\n\ntypedef struct gs_fan_info gs_fan_info;\n\ntypedef struct gs_fan_info gs_fan_info_t;\n\ntypedef struct __gs_common_cmd_ctrl __gs_common_cmd_ctrl;\n\ntypedef struct __gs_common_cmd_ctrl gs_common_cmd_ctrl_t;\n\ntypedef struct gs_camera_cmd_ctrl_t gs_camera_cmd_ctrl_t;\n\ntypedef struct _DUSS_MSG_CAMERA_STATUS_PUSH_t _DUSS_MSG_CAMERA_STATUS_PUSH_t;\n\ntypedef struct _DUSS_MSG_CAMERA_STATUS_PUSH_t DUSS_MSG_CAMERA_STATUS_PUSH_t;\n\ntypedef struct gs_video_channel_manager gs_video_channel_manager;\n\ntypedef struct gs_video_channel_manager gs_video_channel_manager_t;\n\ntypedef struct gs_wl_ctrl gs_wl_ctrl;\n\ntypedef struct gs_wl_ctrl gs_wl_ctrl_t;\n\ntypedef struct gs_av_in_ctrl gs_av_in_ctrl;\n\ntypedef struct gs_av_in_ctrl gs_av_in_ctrl_t;\n\ntypedef struct vdec_local_player vdec_local_player;\n\ntypedef struct vdec_local_player vdec_local_player_t;\n\ntypedef struct metadata_retriever metadata_retriever;\n\ntypedef struct metadata_retriever metadata_retriever_t;\n\ntypedef struct gs_lv_src gs_lv_src;\n\ntypedef struct gs_lv_src gs_lv_src_t;\n\ntypedef struct gs_lv_rec_ctrl gs_lv_rec_ctrl;\n\ntypedef struct gs_lv_rec_ctrl gs_lv_rec_ctrl_t;\n\ntypedef struct gs_usb_gadget_vt gs_usb_gadget_vt;\n\ntypedef struct gs_usb_gadget_vt gs_usb_gadget_vt_t;\n\ntypedef struct gs_lv_transcode gs_lv_transcode;\n\ntypedef struct gs_lv_transcode gs_lv_transcode_t;\n\ntypedef struct gs_aout gs_aout;\n\ntypedef struct gs_aout gs_aout_t;\n\ntypedef struct gs_audio_wl gs_audio_wl;\n\ntypedef struct gs_audio_wl gs_audio_wl_t;\n\ntypedef struct gs_media_cmd_chnl gs_media_cmd_chnl;\n\ntypedef struct gs_media_cmd_chnl gs_media_cmd_chnl_t;\n\ntypedef struct gs_bl gs_bl;\n\ntypedef struct gs_bl gs_bl_t;\n\ntypedef struct timeval timeval;\n\ntypedef uint __uint32_t;\n\ntypedef __uint32_t uint32_t;\n\ntypedef struct __gs_gui __gs_gui;\n\ntypedef struct __gs_gui_config __gs_gui_config;\n\ntypedef struct debug_osd_info debug_osd_info;\n\ntypedef struct debug_osd_info debug_codec_osd_info_t;\n\ntypedef struct debug_cam_osd_info debug_cam_osd_info;\n\ntypedef struct debug_cam_osd_info debug_cam_osd_info_t;\n\ntypedef struct debug_temp_osd_info debug_temp_osd_info;\n\ntypedef struct debug_temp_osd_info debug_temp_osd_info_t;\n\ntypedef struct debug_cp_osd_info debug_cp_osd_info;\n\ntypedef struct debug_cp_osd_info debug_cp_osd_info_t;\n\ntypedef ulonglong __uint64_t;\n\ntypedef __uint64_t uint64_t;\n\ntypedef enum record_mode\n{\n    RECORD_MODE_MANNUAL = 0,\n    RECORD_MODE_LOOPING = 1\n} record_mode;\n\ntypedef enum record_mode record_mode_t;\n\ntypedef enum Record_sender\n{\n    RECORD_BUTTON = 0,\n    RECORD_DISARM = 1\n} Record_sender;\n\ntypedef ushort __uint16_t;\n\ntypedef __uint16_t uint16_t;\n\ntypedef struct gs_battery_voltage gs_battery_voltage;\n\ntypedef struct gs_battery_voltage gs_battery_voltage_t;\n\ntypedef struct __gs_gui_config gs_gui_config_t;\n\ntypedef struct gs_gui_event_t gs_gui_event_t;\n\ntypedef struct __gs_gui gs_gui_t;\n\ntypedef struct gs_ext_fc gs_ext_fc;\n\ntypedef struct gs_ext_fc gs_ext_fc_t;\n\ntypedef struct gs_shram gs_shram;\n\ntypedef struct gs_shram gs_shram_t;\n\ntypedef struct __gs_queue __gs_queue;\n\ntypedef struct __gs_queue gs_queue_t;\n\ntypedef struct gs_user_json_root gs_user_json_root;\n\ntypedef struct gs_user_json_root gs_user_json_root_t;\n\ntypedef struct duss_osal_mutex_handle_t duss_osal_mutex_handle_t;\n\ntypedef struct __gs_avin_test_ctrl __gs_avin_test_ctrl;\n\ntypedef struct __gs_avin_test_ctrl gs_avin_test_ctrl_t;\n\ntypedef struct gs_watermarker_ctrl gs_watermarker_ctrl;\n\ntypedef struct gs_watermarker_ctrl gs_watermarker_ctrl_t;\n\ntypedef struct duss_mb_client duss_mb_client;\n\ntypedef int32_t duss_result_t;\n\ntypedef uint16_t duss_msg_attrib_t;\n\ntypedef uint32_t duss_msg_id_t;\n\ntypedef struct duss_mb_route_item_t duss_mb_route_item_t;\n\ntypedef struct duss_mb_client *duss_mb_client_handle_t;\n\ntypedef struct duss_osal_msgq_handle_t duss_osal_msgq_handle_t;\n\ntypedef struct duss_osal_event_handle_t duss_osal_event_handle_t;\n\ntypedef struct duss_event_ack_identify duss_event_ack_identify;\n\ntypedef struct duss_event_ack_identify duss_event_ack_identify_t;\n\ntypedef struct duss_event_cmd_desc duss_event_cmd_desc;\n\ntypedef struct duss_event duss_event;\n\ntypedef struct duss_event duss_event_t;\n\ntypedef struct duss_event_cmd_desc duss_event_cmd_desc_t;\n\ntypedef struct duss_hal_obj_dev_t duss_hal_obj_dev_t;\n\ntypedef struct product_shm_info product_shm_info;\n\ntypedef struct product_shm_info product_shm_info_t;\n\ntypedef uint size_t;\n\ntypedef struct modem_shmem_info_t modem_shmem_info_t;\n\ntypedef struct racing_debug_info_t racing_debug_info_t;\n\ntypedef struct duss_osal_task_attrib_t duss_osal_task_attrib_t;\n\ntypedef long pthread_t;\n\ntypedef struct duss_storage_client duss_storage_client;\n\ntypedef struct duss_storage_client duss_storage_client_t;\n\ntypedef enum sd_file_type\n{\n    SD_FILE_TYPE_UNKNOWN = 0,\n    SD_FILE_TYPE_FAT32 = 1,\n    SD_FILE_TYPE_EXFAT = 2,\n    SD_FILE_TYPE_MAX = 3\n} sd_file_type;\n\ntypedef enum sd_file_type sd_file_type_t;\n\ntypedef struct gs_storage_listener gs_storage_listener;\n\ntypedef enum storage_event\n{\n    STORAGE_EVENT_NONE = 0,\n    STORAGE_EVENT_SPACE_LOW = 1\n} storage_event;\n\ntypedef enum storage_event storage_event_t;\n\ntypedef struct gs_storage_listener gs_storage_listener_t;\n\ntypedef struct gs_sd_listener gs_sd_listener;\n\ntypedef enum sd_event\n{\n    SD_EVENT_NONE = 0,\n    SD_EVENT_SPACE_LOW = 1,\n    SD_EVENT_SD_INSERT = 2,\n    SD_EVENT_SD_REMOVE = 3\n} sd_event;\n\ntypedef enum sd_event sd_event_t;\n\ntypedef struct gs_sd_listener gs_sd_listener_t;\n\ntypedef struct gs_meta_listener gs_meta_listener;\n\ntypedef enum meta_event\n{\n    META_EVENT_NONE = 0,\n    META_EVENT_SD_INSERT = 1,\n    META_EVENT_SD_REMOVE = 2,\n    META_EVENT_SD_FORMAT = 3\n} meta_event;\n\ntypedef enum meta_event meta_event_t;\n\ntypedef struct gs_meta_listener gs_meta_listener_t;\n\ntypedef struct gs_playback_listener gs_playback_listener;\n\ntypedef enum playback_event\n{\n    PB_EVENT_NONE = 0,\n    PB_EVENT_SD_INSERT = 1,\n    PB_EVENT_SD_REMOVE = 2,\n    PB_EVENT_SD_FORMAT = 3\n} playback_event;\n\ntypedef enum playback_event playback_event_t;\n\ntypedef struct gs_playback_listener gs_playback_listener_t;\n\ntypedef struct sem_t sem_t;\n\ntypedef struct timespec timespec;\n\ntypedef struct gs_modem_link_listener gs_modem_link_listener;\n\ntypedef struct gs_modem_link_listener gs_modem_link_listener_t;\n\ntypedef struct glass_signal_quality_t glass_signal_quality_t;\n\ntypedef struct _DUSS_MSG_RACING_CHANNEL_SCAN_REQ_t _DUSS_MSG_RACING_CHANNEL_SCAN_REQ_t;\n\ntypedef struct _DUSS_MSG_RACING_CHANNEL_SCAN_REQ_t DUSS_MSG_RACING_CHANNEL_SCAN_REQ_t;\n\ntypedef struct _DUSS_MSG_RACING_CHANNEL_SCAN_INFO_t _DUSS_MSG_RACING_CHANNEL_SCAN_INFO_t;\n\ntypedef struct _DUSS_MSG_RACING_CHANNEL_SCAN_INFO_t DUSS_MSG_RACING_CHANNEL_SCAN_INFO_t;\n\ntypedef struct _DUSS_MSG_RACING_CHANNEL_ROB_INFO_t _DUSS_MSG_RACING_CHANNEL_ROB_INFO_t;\n\ntypedef struct _DUSS_MSG_RACING_CHANNEL_ROB_INFO_t DUSS_MSG_RACING_CHANNEL_ROB_INFO_t;\n\ntypedef struct _DUSS_MSG_RACING_PHY_CHECK_REQ_t _DUSS_MSG_RACING_PHY_CHECK_REQ_t;\n\ntypedef struct _DUSS_MSG_RACING_PHY_CHECK_REQ_t DUSS_MSG_RACING_PHY_CHECK_REQ_t;\n\ntypedef struct _DUSS_MSG_RACING_PHY_CHECK_INFO_t _DUSS_MSG_RACING_PHY_CHECK_INFO_t;\n\ntypedef struct _DUSS_MSG_RACING_PHY_CHECK_INFO_t DUSS_MSG_RACING_PHY_CHECK_INFO_t;\n\ntypedef enum gs_modem_scan_type_t\n{\n    GS_MODEM_SCAN_ISM = 0,\n    GS_MODEM_SCAN_HAM = 1,\n    GS_MODEM_SCAN_JAPAN_HAM_5G = 2,\n    GS_MODEM_SCAN_HAM_MATCH = 3,\n    GS_MODEM_SCAN_HAM_40MHz = 4,\n    GS_MODEM_SCAN_HAM_40MHz_EXTRA = 5,\n    GS_MODEM_SCAN_UKNOWN = 6\n} gs_modem_scan_type_t;\n\ntypedef union rc_set_reverse_t rc_set_reverse_t;\n\ntypedef struct rc_set_all_st_t rc_set_all_st_t;\n\ntypedef struct rc_set_all_ep_t rc_set_all_ep_t;\n\ntypedef struct rc_set_all_st_and_rev_t rc_set_all_st_and_rev_t;\n\ntypedef struct dummy_ui_ctx_t dummy_ui_ctx_t;\n\ntypedef void (*duss_osal_timer_entry_t)(void *);\n\ntypedef enum duss_osal_priority_t\n{\n    DUSS_OSAL_PRI_NORMAL = 0,\n    DUSS_OSAL_PRI_LOWEST = 1,\n    DUSS_OSAL_PRI_LOW1 = 3,\n    DUSS_OSAL_PRI_LOW2 = 6,\n    DUSS_OSAL_PRI_LOW3 = 9,\n    DUSS_OSAL_PRI_MID1 = 13,\n    DUSS_OSAL_PRI_MID2 = 16,\n    DUSS_OSAL_PRI_MID3 = 19,\n    DUSS_OSAL_PRI_HIGH1 = 23,\n    DUSS_OSAL_PRI_HIGH2 = 26,\n    DUSS_OSAL_PRI_HIGH3 = 29,\n    DUSS_OSAL_PRI_TIM1 = 33,\n    DUSS_OSAL_PRI_TIM2 = 36,\n    DUSS_OSAL_PRI_TIM3 = 39,\n    DUSS_OSAL_PRI_INT = 50,\n    DUSS_OSAL_PRI_HIGHEST = 99\n} duss_osal_priority_t;\n\ntypedef struct factory_check factory_check;\n\ntypedef struct anon_struct_conflictc3fb_for_sw anon_struct_conflictc3fb_for_sw;\n\ntypedef struct anon_struct_conflictc431_for_key anon_struct_conflictc431_for_key;\n\ntypedef struct duss_osal_mutex_attrib_t duss_osal_mutex_attrib_t;\n\ntypedef enum gs_fan_level_t\n{\n    GS_FAN_LEVEL_0 = 0,\n    GS_FAN_LEVEL_1 = 1,\n    GS_FAN_LEVEL_2 = 2,\n    GS_FAN_LEVEL_3 = 3,\n    GS_FAN_LEVEL_4 = 4,\n    GS_FAN_LEVEL_5 = 5,\n    GS_FAN_LEVEL_MAX = 6\n} gs_fan_level_t;\n\ntypedef struct _DUSS_MSG_SYSTEM_STATE_t _DUSS_MSG_SYSTEM_STATE_t;\n\ntypedef struct _DUSS_MSG_SYSTEM_STATE_t DUSS_MSG_SYSTEM_STATE_t;\n\ntypedef struct _DUSS_MSG_F_INDEX_MODE_t _DUSS_MSG_F_INDEX_MODE_t;\n\ntypedef struct _DUSS_MSG_F_INDEX_MODE_t DUSS_MSG_F_INDEX_MODE_t;\n\ntypedef struct _DUSS_MSG_QUICKVIEW_t _DUSS_MSG_QUICKVIEW_t;\n\ntypedef struct _DUSS_MSG_QUICKVIEW_t DUSS_MSG_QUICKVIEW_t;\n\ntypedef struct _DUSS_MSG_PHOTO_OSD_PARA_ _DUSS_MSG_PHOTO_OSD_PARA_;\n\ntypedef struct _DUSS_MSG_PHOTO_OSD_PARA_ DUSS_MSG_PHOTO_OSD_PARA_t;\n\ntypedef struct _DUSS_MSG_PREVIEW_OSD_PARA_t _DUSS_MSG_PREVIEW_OSD_PARA_t;\n\ntypedef struct _DUSS_MSG_PREVIEW_OSD_PARA_t DUSS_MSG_PREVIEW_OSD_PARA_t;\n\ntypedef struct _DUSS_MSG_HISTOGRAM_t _DUSS_MSG_HISTOGRAM_t;\n\ntypedef struct _DUSS_MSG_HISTOGRAM_t DUSS_MSG_HISTOGRAM_t;\n\ntypedef struct _DUSS_MSG_CAMERA_AUDIO_STATUS_t _DUSS_MSG_CAMERA_AUDIO_STATUS_t;\n\ntypedef struct _DUSS_MSG_CAMERA_AUDIO_STATUS_t DUSS_MSG_CAMERA_AUDIO_STATUS_t;\n\ntypedef struct _DUSS_MSG_HYPERLAPSE_LIVEVIEW_MARGIN_t _DUSS_MSG_HYPERLAPSE_LIVEVIEW_MARGIN_t;\n\ntypedef struct _DUSS_MSG_HYPERLAPSE_LIVEVIEW_MARGIN_t DUSS_MSG_HYPERLAPSE_LIVEVIEW_MARGIN_t;\n\ntypedef struct gs_wl_channel gs_wl_channel;\n\ntypedef struct gs_wl_channel gs_wl_channel_t;\n\ntypedef struct gs_local_playback_channel gs_local_playback_channel;\n\ntypedef struct gs_local_playback_channel gs_local_playback_channel_t;\n\ntypedef struct gs_av_in_channel gs_av_in_channel;\n\ntypedef struct gs_av_in_channel gs_av_in_channel_t;\n\ntypedef struct gs_rc_setting_channel gs_rc_setting_channel;\n\ntypedef struct gs_rc_setting_channel gs_rc_setting_channel_t;\n\ntypedef struct gs_csi_channel gs_csi_channel;\n\ntypedef struct gs_csi_channel gs_csi_channel_t;\n\ntypedef struct gs_non_video_channel gs_non_video_channel;\n\ntypedef struct gs_non_video_channel gs_non_video_channel_t;\n\ntypedef struct gs_video_channel gs_video_channel;\n\ntypedef struct gs_video_channel_message gs_video_channel_message;\n\ntypedef struct gs_video_channel_message gs_video_channel_message_t;\n\ntypedef struct gs_video_channel gs_video_channel_t;\n\ntypedef struct gs_video_channel_id gs_video_channel_id;\n\ntypedef struct gs_video_channel_id gs_video_channel_id_t;\n\ntypedef void (*gs_video_channel_switch_callback_t)(void *, gs_video_channel_id_t *);\n\ntypedef void (*gs_video_channel_push_callback_t)(void *, gs_video_channel_id_t *);\n\ntypedef struct gs_avin_us gs_avin_us;\n\ntypedef struct gs_avin_us gs_avin_us_t;\n\ntypedef struct AVFormatContext AVFormatContext;\n\ntypedef struct audio_dec audio_dec;\n\ntypedef enum decoder_event\n{\n    DEC_EVENT_INVALID = 0,\n    DEC_EVENT_A_EOS = 1,\n    DEC_EVENT_V_EOS = 2\n} decoder_event;\n\ntypedef enum decoder_event dec_event_t;\n\ntypedef struct audio_dec audio_dec_t;\n\ntypedef struct cp_vdec cp_vdec;\n\ntypedef struct cp_vdec cp_vdec_t;\n\ntypedef struct vdec_video_file_info vdec_video_file_info;\n\ntypedef struct vdec_video_file_info vdec_video_file_info_t;\n\ntypedef longlong __int64_t;\n\ntypedef __int64_t int64_t;\n\ntypedef struct sqlite3 sqlite3;\n\ntypedef struct gs_lv_csm gs_lv_csm;\n\ntypedef struct gs_lv_pkt gs_lv_pkt;\n\ntypedef struct gs_lv_pkt gs_lv_pkt_t;\n\ntypedef struct gs_lv_csm gs_lv_csm_t;\n\ntypedef struct duss_list_head duss_list_head;\n\ntypedef struct gs_usb_listener gs_usb_listener;\n\ntypedef enum usb_event\n{\n    USB_EVENT_NONE = 0,\n    USB_EVENT_GADGET_CONNECTED = 1\n} usb_event;\n\ntypedef enum usb_event usb_event_t;\n\ntypedef struct gs_usb_listener gs_usb_listener_t;\n\ntypedef struct ion_info ion_info;\n\ntypedef struct ion_info ion_info_t;\n\ntypedef enum record_state\n{\n    RECORD_STATE_IDLE = 0,\n    RECORD_STATE_RECORDING = 1,\n    RECORD_STATE_STOP_ERROR = 2,\n    RECORD_STATE_STOP_FULL = 3,\n    RECORD_STATE_STOP_WRITE_SLOW = 4,\n    RECORD_STATE_STARTING = 5,\n    RECORD_STATE_STOPPING = 6\n} record_state;\n\ntypedef enum record_state record_state_t;\n\ntypedef struct AVCodecContext AVCodecContext;\n\ntypedef struct AVFrame AVFrame;\n\ntypedef enum AVPixelFormat\n{\n    AV_PIX_FMT_NONE = -1,\n    PIX_FMT_NONE = -1,\n    AV_PIX_FMT_YUV420P = 0,\n    PIX_FMT_YUV420P = 0,\n    AV_PIX_FMT_YUYV422 = 1,\n    PIX_FMT_YUYV422 = 1,\n    AV_PIX_FMT_RGB24 = 2,\n    PIX_FMT_RGB24 = 2,\n    AV_PIX_FMT_BGR24 = 3,\n    PIX_FMT_BGR24 = 3,\n    AV_PIX_FMT_YUV422P = 4,\n    PIX_FMT_YUV422P = 4,\n    AV_PIX_FMT_YUV444P = 5,\n    PIX_FMT_YUV444P = 5,\n    AV_PIX_FMT_YUV410P = 6,\n    PIX_FMT_YUV410P = 6,\n    AV_PIX_FMT_YUV411P = 7,\n    PIX_FMT_YUV411P = 7,\n    AV_PIX_FMT_GRAY8 = 8,\n    PIX_FMT_GRAY8 = 8,\n    AV_PIX_FMT_MONOWHITE = 9,\n    PIX_FMT_MONOWHITE = 9,\n    AV_PIX_FMT_MONOBLACK = 10,\n    PIX_FMT_MONOBLACK = 10,\n    AV_PIX_FMT_PAL8 = 11,\n    PIX_FMT_PAL8 = 11,\n    AV_PIX_FMT_YUVJ420P = 12,\n    PIX_FMT_YUVJ420P = 12,\n    AV_PIX_FMT_YUVJ422P = 13,\n    PIX_FMT_YUVJ422P = 13,\n    AV_PIX_FMT_YUVJ444P = 14,\n    PIX_FMT_YUVJ444P = 14,\n    AV_PIX_FMT_XVMC_MPEG2_MC = 15,\n    PIX_FMT_XVMC_MPEG2_MC = 15,\n    AV_PIX_FMT_XVMC_MPEG2_IDCT = 16,\n    PIX_FMT_XVMC_MPEG2_IDCT = 16,\n    AV_PIX_FMT_UYVY422 = 17,\n    PIX_FMT_UYVY422 = 17,\n    AV_PIX_FMT_UYYVYY411 = 18,\n    PIX_FMT_UYYVYY411 = 18,\n    AV_PIX_FMT_BGR8 = 19,\n    PIX_FMT_BGR8 = 19,\n    AV_PIX_FMT_BGR4 = 20,\n    PIX_FMT_BGR4 = 20,\n    AV_PIX_FMT_BGR4_BYTE = 21,\n    PIX_FMT_BGR4_BYTE = 21,\n    AV_PIX_FMT_RGB8 = 22,\n    PIX_FMT_RGB8 = 22,\n    AV_PIX_FMT_RGB4 = 23,\n    PIX_FMT_RGB4 = 23,\n    AV_PIX_FMT_RGB4_BYTE = 24,\n    PIX_FMT_RGB4_BYTE = 24,\n    AV_PIX_FMT_NV12 = 25,\n    PIX_FMT_NV12 = 25,\n    AV_PIX_FMT_NV21 = 26,\n    PIX_FMT_NV21 = 26,\n    AV_PIX_FMT_ARGB = 27,\n    PIX_FMT_ARGB = 27,\n    AV_PIX_FMT_RGBA = 28,\n    PIX_FMT_RGBA = 28,\n    AV_PIX_FMT_ABGR = 29,\n    PIX_FMT_ABGR = 29,\n    AV_PIX_FMT_BGRA = 30,\n    PIX_FMT_BGRA = 30,\n    AV_PIX_FMT_GRAY16BE = 31,\n    PIX_FMT_GRAY16BE = 31,\n    AV_PIX_FMT_GRAY16LE = 32,\n    PIX_FMT_GRAY16LE = 32,\n    AV_PIX_FMT_YUV440P = 33,\n    PIX_FMT_YUV440P = 33,\n    AV_PIX_FMT_YUVJ440P = 34,\n    PIX_FMT_YUVJ440P = 34,\n    AV_PIX_FMT_YUVA420P = 35,\n    PIX_FMT_YUVA420P = 35,\n    AV_PIX_FMT_VDPAU_H264 = 36,\n    PIX_FMT_VDPAU_H264 = 36,\n    AV_PIX_FMT_VDPAU_MPEG1 = 37,\n    PIX_FMT_VDPAU_MPEG1 = 37,\n    AV_PIX_FMT_VDPAU_MPEG2 = 38,\n    PIX_FMT_VDPAU_MPEG2 = 38,\n    AV_PIX_FMT_VDPAU_WMV3 = 39,\n    PIX_FMT_VDPAU_WMV3 = 39,\n    AV_PIX_FMT_VDPAU_VC1 = 40,\n    PIX_FMT_VDPAU_VC1 = 40,\n    AV_PIX_FMT_RGB48BE = 41,\n    PIX_FMT_RGB48BE = 41,\n    AV_PIX_FMT_RGB48LE = 42,\n    PIX_FMT_RGB48LE = 42,\n    AV_PIX_FMT_RGB565BE = 43,\n    PIX_FMT_RGB565BE = 43,\n    AV_PIX_FMT_RGB565LE = 44,\n    PIX_FMT_RGB565LE = 44,\n    AV_PIX_FMT_RGB555BE = 45,\n    PIX_FMT_RGB555BE = 45,\n    AV_PIX_FMT_RGB555LE = 46,\n    PIX_FMT_RGB555LE = 46,\n    AV_PIX_FMT_BGR565BE = 47,\n    PIX_FMT_BGR565BE = 47,\n    AV_PIX_FMT_BGR565LE = 48,\n    PIX_FMT_BGR565LE = 48,\n    AV_PIX_FMT_BGR555BE = 49,\n    PIX_FMT_BGR555BE = 49,\n    AV_PIX_FMT_BGR555LE = 50,\n    PIX_FMT_BGR555LE = 50,\n    AV_PIX_FMT_VAAPI_MOCO = 51,\n    PIX_FMT_VAAPI_MOCO = 51,\n    AV_PIX_FMT_VAAPI_IDCT = 52,\n    PIX_FMT_VAAPI_IDCT = 52,\n    AV_PIX_FMT_VAAPI_VLD = 53,\n    PIX_FMT_VAAPI_VLD = 53,\n    AV_PIX_FMT_YUV420P16LE = 54,\n    PIX_FMT_YUV420P16LE = 54,\n    AV_PIX_FMT_YUV420P16BE = 55,\n    PIX_FMT_YUV420P16BE = 55,\n    AV_PIX_FMT_YUV422P16LE = 56,\n    PIX_FMT_YUV422P16LE = 56,\n    AV_PIX_FMT_YUV422P16BE = 57,\n    PIX_FMT_YUV422P16BE = 57,\n    AV_PIX_FMT_YUV444P16LE = 58,\n    PIX_FMT_YUV444P16LE = 58,\n    AV_PIX_FMT_YUV444P16BE = 59,\n    PIX_FMT_YUV444P16BE = 59,\n    AV_PIX_FMT_VDPAU_MPEG4 = 60,\n    PIX_FMT_VDPAU_MPEG4 = 60,\n    AV_PIX_FMT_DXVA2_VLD = 61,\n    PIX_FMT_DXVA2_VLD = 61,\n    AV_PIX_FMT_RGB444LE = 62,\n    PIX_FMT_RGB444LE = 62,\n    AV_PIX_FMT_RGB444BE = 63,\n    PIX_FMT_RGB444BE = 63,\n    AV_PIX_FMT_BGR444LE = 64,\n    PIX_FMT_BGR444LE = 64,\n    AV_PIX_FMT_BGR444BE = 65,\n    PIX_FMT_BGR444BE = 65,\n    AV_PIX_FMT_GRAY8A = 66,\n    AV_PIX_FMT_Y400A = 66,\n    AV_PIX_FMT_YA8 = 66,\n    PIX_FMT_GRAY8A = 66,\n    AV_PIX_FMT_BGR48BE = 67,\n    PIX_FMT_BGR48BE = 67,\n    AV_PIX_FMT_BGR48LE = 68,\n    PIX_FMT_BGR48LE = 68,\n    AV_PIX_FMT_YUV420P9BE = 69,\n    PIX_FMT_YUV420P9BE = 69,\n    AV_PIX_FMT_YUV420P9LE = 70,\n    PIX_FMT_YUV420P9LE = 70,\n    AV_PIX_FMT_YUV420P10BE = 71,\n    PIX_FMT_YUV420P10BE = 71,\n    AV_PIX_FMT_YUV420P10LE = 72,\n    PIX_FMT_YUV420P10LE = 72,\n    AV_PIX_FMT_YUV422P10BE = 73,\n    PIX_FMT_YUV422P10BE = 73,\n    AV_PIX_FMT_YUV422P10LE = 74,\n    PIX_FMT_YUV422P10LE = 74,\n    AV_PIX_FMT_YUV444P9BE = 75,\n    PIX_FMT_YUV444P9BE = 75,\n    AV_PIX_FMT_YUV444P9LE = 76,\n    PIX_FMT_YUV444P9LE = 76,\n    AV_PIX_FMT_YUV444P10BE = 77,\n    PIX_FMT_YUV444P10BE = 77,\n    AV_PIX_FMT_YUV444P10LE = 78,\n    PIX_FMT_YUV444P10LE = 78,\n    AV_PIX_FMT_YUV422P9BE = 79,\n    PIX_FMT_YUV422P9BE = 79,\n    AV_PIX_FMT_YUV422P9LE = 80,\n    PIX_FMT_YUV422P9LE = 80,\n    AV_PIX_FMT_VDA_VLD = 81,\n    PIX_FMT_VDA_VLD = 81,\n    AV_PIX_FMT_GBRP = 82,\n    PIX_FMT_GBRP = 82,\n    AV_PIX_FMT_GBRP9BE = 83,\n    PIX_FMT_GBRP9BE = 83,\n    AV_PIX_FMT_GBRP9LE = 84,\n    PIX_FMT_GBRP9LE = 84,\n    AV_PIX_FMT_GBRP10BE = 85,\n    PIX_FMT_GBRP10BE = 85,\n    AV_PIX_FMT_GBRP10LE = 86,\n    PIX_FMT_GBRP10LE = 86,\n    AV_PIX_FMT_GBRP16BE = 87,\n    PIX_FMT_GBRP16BE = 87,\n    AV_PIX_FMT_GBRP16LE = 88,\n    PIX_FMT_GBRP16LE = 88,\n    AV_PIX_FMT_YUVA422P_LIBAV = 89,\n    AV_PIX_FMT_YUVA444P_LIBAV = 90,\n    AV_PIX_FMT_YUVA420P9BE = 91,\n    AV_PIX_FMT_YUVA420P9LE = 92,\n    AV_PIX_FMT_YUVA422P9BE = 93,\n    AV_PIX_FMT_YUVA422P9LE = 94,\n    AV_PIX_FMT_YUVA444P9BE = 95,\n    AV_PIX_FMT_YUVA444P9LE = 96,\n    AV_PIX_FMT_YUVA420P10BE = 97,\n    AV_PIX_FMT_YUVA420P10LE = 98,\n    AV_PIX_FMT_YUVA422P10BE = 99,\n    AV_PIX_FMT_YUVA422P10LE = 100,\n    AV_PIX_FMT_YUVA444P10BE = 101,\n    AV_PIX_FMT_YUVA444P10LE = 102,\n    AV_PIX_FMT_YUVA420P16BE = 103,\n    AV_PIX_FMT_YUVA420P16LE = 104,\n    AV_PIX_FMT_YUVA422P16BE = 105,\n    AV_PIX_FMT_YUVA422P16LE = 106,\n    AV_PIX_FMT_YUVA444P16BE = 107,\n    AV_PIX_FMT_YUVA444P16LE = 108,\n    AV_PIX_FMT_VDPAU = 109,\n    AV_PIX_FMT_XYZ12LE = 110,\n    AV_PIX_FMT_XYZ12BE = 111,\n    AV_PIX_FMT_NV16 = 112,\n    AV_PIX_FMT_NV20LE = 113,\n    AV_PIX_FMT_NV20BE = 114,\n    AV_PIX_FMT_RGBA64BE_LIBAV = 115,\n    AV_PIX_FMT_RGBA64LE_LIBAV = 116,\n    AV_PIX_FMT_BGRA64BE_LIBAV = 117,\n    AV_PIX_FMT_BGRA64LE_LIBAV = 118,\n    AV_PIX_FMT_YVYU422 = 119,\n    AV_PIX_FMT_VDA = 120,\n    AV_PIX_FMT_YA16BE = 121,\n    AV_PIX_FMT_YA16LE = 122,\n    AV_PIX_FMT_RGBA64BE = 291,\n    PIX_FMT_RGBA64BE = 291,\n    AV_PIX_FMT_RGBA64LE = 292,\n    PIX_FMT_RGBA64LE = 292,\n    AV_PIX_FMT_BGRA64BE = 293,\n    PIX_FMT_BGRA64BE = 293,\n    AV_PIX_FMT_BGRA64LE = 294,\n    PIX_FMT_BGRA64LE = 294,\n    AV_PIX_FMT_0RGB = 295,\n    PIX_FMT_0RGB = 295,\n    AV_PIX_FMT_RGB0 = 296,\n    PIX_FMT_RGB0 = 296,\n    AV_PIX_FMT_0BGR = 297,\n    PIX_FMT_0BGR = 297,\n    AV_PIX_FMT_BGR0 = 298,\n    PIX_FMT_BGR0 = 298,\n    AV_PIX_FMT_YUVA444P = 299,\n    PIX_FMT_YUVA444P = 299,\n    AV_PIX_FMT_YUVA422P = 300,\n    PIX_FMT_YUVA422P = 300,\n    AV_PIX_FMT_YUV420P12BE = 301,\n    PIX_FMT_YUV420P12BE = 301,\n    AV_PIX_FMT_YUV420P12LE = 302,\n    PIX_FMT_YUV420P12LE = 302,\n    AV_PIX_FMT_YUV420P14BE = 303,\n    PIX_FMT_YUV420P14BE = 303,\n    AV_PIX_FMT_YUV420P14LE = 304,\n    PIX_FMT_YUV420P14LE = 304,\n    AV_PIX_FMT_YUV422P12BE = 305,\n    PIX_FMT_YUV422P12BE = 305,\n    AV_PIX_FMT_YUV422P12LE = 306,\n    PIX_FMT_YUV422P12LE = 306,\n    AV_PIX_FMT_YUV422P14BE = 307,\n    PIX_FMT_YUV422P14BE = 307,\n    AV_PIX_FMT_YUV422P14LE = 308,\n    PIX_FMT_YUV422P14LE = 308,\n    AV_PIX_FMT_YUV444P12BE = 309,\n    PIX_FMT_YUV444P12BE = 309,\n    AV_PIX_FMT_YUV444P12LE = 310,\n    PIX_FMT_YUV444P12LE = 310,\n    AV_PIX_FMT_YUV444P14BE = 311,\n    PIX_FMT_YUV444P14BE = 311,\n    AV_PIX_FMT_YUV444P14LE = 312,\n    PIX_FMT_YUV444P14LE = 312,\n    AV_PIX_FMT_GBRP12BE = 313,\n    PIX_FMT_GBRP12BE = 313,\n    AV_PIX_FMT_GBRP12LE = 314,\n    PIX_FMT_GBRP12LE = 314,\n    AV_PIX_FMT_GBRP14BE = 315,\n    PIX_FMT_GBRP14BE = 315,\n    AV_PIX_FMT_GBRP14LE = 316,\n    PIX_FMT_GBRP14LE = 316,\n    AV_PIX_FMT_GBRAP = 317,\n    PIX_FMT_NB = 317,\n    AV_PIX_FMT_GBRAP16BE = 318,\n    AV_PIX_FMT_GBRAP16LE = 319,\n    AV_PIX_FMT_YUVJ411P = 320,\n    AV_PIX_FMT_BAYER_BGGR8 = 321,\n    AV_PIX_FMT_BAYER_RGGB8 = 322,\n    AV_PIX_FMT_BAYER_GBRG8 = 323,\n    AV_PIX_FMT_BAYER_GRBG8 = 324,\n    AV_PIX_FMT_BAYER_BGGR16LE = 325,\n    AV_PIX_FMT_BAYER_BGGR16BE = 326,\n    AV_PIX_FMT_BAYER_RGGB16LE = 327,\n    AV_PIX_FMT_BAYER_RGGB16BE = 328,\n    AV_PIX_FMT_BAYER_GBRG16LE = 329,\n    AV_PIX_FMT_BAYER_GBRG16BE = 330,\n    AV_PIX_FMT_BAYER_GRBG16LE = 331,\n    AV_PIX_FMT_BAYER_GRBG16BE = 332,\n    AV_PIX_FMT_NB = 333\n} AVPixelFormat;\n\ntypedef struct pl_muxer_t pl_muxer_t;\n\ntypedef struct pl_muxer_t *p1_muxer_handle_t;\n\ntypedef struct __sFILE __sFILE;\n\ntypedef long __kernel_long_t;\n\ntypedef __kernel_long_t __kernel_off_t;\n\ntypedef __kernel_off_t off_t;\n\ntypedef off_t fpos_t;\n\ntypedef struct __sFILE FILE;\n\ntypedef struct pcm_config pcm_config;\n\ntypedef struct pcm pcm;\n\ntypedef __kernel_long_t __kernel_time_t;\n\ntypedef __kernel_long_t __kernel_suseconds_t;\n\ntypedef struct ext_fc_ops ext_fc_ops;\n\ntypedef enum batteryState_e\n{\n    BATTERY_OK = 0,\n    BATTERY_WARNING = 1,\n    BATTERY_CRITICAL = 2,\n    BATTERY_NOT_PRESENT = 3,\n    BATTERY_INIT = 4\n} batteryState_e;\n\ntypedef struct DUSS_MSG_FC_RACING_DRONE_OSD_PUSH DUSS_MSG_FC_RACING_DRONE_OSD_PUSH;\n\ntypedef struct DUSS_MSG_FC_RACING_DRONE_OSD_PUSH DUSS_MSG_FC_RACING_DRONE_OSD_PUSH_t;\n\ntypedef struct DUSS_MSG_EXT_FC_RTC DUSS_MSG_EXT_FC_RTC;\n\ntypedef struct DUSS_MSG_EXT_FC_RTC DUSS_MSG_EXT_FC_RTC_t;\n\ntypedef struct ext_fc_ops ext_fc_ops_t;\n\ntypedef struct vr_device_ops vr_device_ops;\n\ntypedef short __int16_t;\n\ntypedef __int16_t int16_t;\n\ntypedef struct vr_device_ops vr_device_ops_t;\n\ntypedef struct local_playback_ops local_playback_ops;\n\ntypedef struct local_playback_ops local_playback_ops_t;\n\ntypedef struct uav_camera_ops uav_camera_ops;\n\ntypedef struct __gs_camera_cmd __gs_camera_cmd;\n\ntypedef struct __gs_camera_cmd gs_camera_cmd_t;\n\ntypedef struct uav_camera_ops uav_camera_ops_t;\n\ntypedef struct uav_gimbal_ops uav_gimbal_ops;\n\ntypedef struct uav_gimbal_ops uav_gimbal_ops_t;\n\ntypedef struct modem_ops modem_ops;\n\ntypedef enum gs_link_stat_t\n{\n    GS_LINK_STAT_NORMAL = 0,\n    GS_LINK_STAT_WEAK = 1,\n    GS_LINK_STAT_LOST = 2,\n    GS_LINK_STAT_UKNOWN = 3\n} gs_link_stat_t;\n\ntypedef struct modem_ops modem_ops_t;\n\ntypedef struct rc_ops rc_ops;\n\ntypedef struct rc_set_function_pack_t rc_set_function_pack_t;\n\ntypedef struct rc_set_stick_mode_pack_t rc_set_stick_mode_pack_t;\n\ntypedef struct rc_set_warning_mode_pack_t rc_set_warning_mode_pack_t;\n\ntypedef struct rc_monitor_pack_t rc_monitor_pack_t;\n\ntypedef struct rc_ops rc_ops_t;\n\ntypedef struct vcm_ops vcm_ops;\n\ntypedef enum gs_main_channel_id\n{\n    GS_FIRST_VIDEO_CHANNEL = 0,\n    GS_WL_CHANNEL = 0,\n    GS_LOCAL_PLAYBACK_CHANNEL = 1,\n    GS_AV_IN_CHANNEL = 2,\n    GS_RC_SETTING_CHANNEL = 3,\n    GS_CSI_CHANNEL = 4,\n    GS_NONE_VIDEO_CHANNEL = 5,\n    GS_VIDEO_CHANNEL_COUNT = 6,\n    GS_UNKNOWN_VIDEO_CHANNEL = 7\n} gs_main_channel_id;\n\ntypedef enum gs_main_channel_id gs_main_channel_id_t;\n\ntypedef struct vcm_ops vcm_ops_t;\n\ntypedef struct common_cmd_ops common_cmd_ops;\n\ntypedef struct __gs_common_cmd __gs_common_cmd;\n\ntypedef struct __gs_common_cmd gs_common_cmd_t;\n\ntypedef struct common_cmd_ops common_cmd_ops_t;\n\ntypedef struct debug_osd_item debug_osd_item;\n\ntypedef struct debug_osd_item debug_osd_item_t;\n\ntypedef uint gs_gui_event_type_t;\n\ntypedef uint gs_gui_event_symbol_t;\n\ntypedef struct DUSS_MSG_EXT_FC_PID DUSS_MSG_EXT_FC_PID;\n\ntypedef struct DUSS_MSG_EXT_FC_PID DUSS_MSG_EXT_FC_PID_t;\n\ntypedef struct DUSS_MSG_EXT_FC_AUX DUSS_MSG_EXT_FC_AUX;\n\ntypedef struct DUSS_MSG_EXT_FC_AUX DUSS_MSG_EXT_FC_AUX_t;\n\ntypedef struct DUSS_MSG_EXT_FC_RATE DUSS_MSG_EXT_FC_RATE;\n\ntypedef struct DUSS_MSG_EXT_FC_RATE DUSS_MSG_EXT_FC_RATE_t;\n\ntypedef struct DUSS_MSG_EXT_FC_SERVO DUSS_MSG_EXT_FC_SERVO;\n\ntypedef struct DUSS_MSG_EXT_FC_SERVO DUSS_MSG_EXT_FC_SERVO_t;\n\ntypedef struct DUSS_MSG_EXT_FC_FILTER DUSS_MSG_EXT_FC_FILTER;\n\ntypedef struct DUSS_MSG_EXT_FC_FILTER DUSS_MSG_EXT_FC_FILTER_t;\n\ntypedef struct DUSS_MSG_EXT_FC_ADVANCED_PID DUSS_MSG_EXT_FC_ADVANCED_PID;\n\ntypedef struct DUSS_MSG_EXT_FC_ADVANCED_PID DUSS_MSG_EXT_FC_ADVANCED_PID_t;\n\ntypedef struct DUSS_MSG_EXT_FC_MSP_STATUS DUSS_MSG_EXT_FC_MSP_STATUS;\n\ntypedef struct DUSS_MSG_EXT_FC_MSP_STATUS DUSS_MSG_EXT_FC_MSP_STATUS_t;\n\ntypedef struct DUSS_MSG_EXT_FC_RC DUSS_MSG_EXT_FC_RC;\n\ntypedef struct DUSS_MSG_EXT_FC_RC DUSS_MSG_EXT_FC_RC_t;\n\ntypedef struct DUSS_MSG_EXT_FC_BATTERY_STATE DUSS_MSG_EXT_FC_BATTERY_STATE;\n\ntypedef struct DUSS_MSG_EXT_FC_BATTERY_STATE DUSS_MSG_EXT_FC_BATTERY_STATE_t;\n\ntypedef struct DUSS_MSG_EXT_FC_OSD_CONFIG DUSS_MSG_EXT_FC_OSD_CONFIG;\n\ntypedef struct DUSS_MSG_EXT_FC_OSD_CONFIG DUSS_MSG_EXT_FC_OSD_CONFIG_t;\n\ntypedef struct DUSS_MSG_EXT_FC_ESC_DATA DUSS_MSG_EXT_FC_ESC_DATA;\n\ntypedef struct DUSS_MSG_EXT_FC_ESC_DATA DUSS_MSG_EXT_FC_ESC_DATA_t;\n\ntypedef struct DUSS_MSG_EXT_FC_VERSION DUSS_MSG_EXT_FC_VERSION;\n\ntypedef struct DUSS_MSG_EXT_FC_VERSION DUSS_MSG_EXT_FC_VERSION_t;\n\ntypedef enum GS_EXT_FC_OSD_STATUS\n{\n    OSD_CONFIG_REQUEST_UPDATE = 0,\n    OSD_CONFIG_UPDATE_COMPLETE = 1\n} GS_EXT_FC_OSD_STATUS;\n\ntypedef struct cJSON cJSON;\n\ntypedef struct gs_watermarker_us gs_watermarker_us;\n\ntypedef struct gs_watermarker_us gs_watermarker_us_t;\n\ntypedef uint8_t duss_mb_channel_t;\n\ntypedef uint8_t duss_mb_pack_ver_t;\n\ntypedef union anon_union_conflict1232_for_channel anon_union_conflict1232_for_channel;\n\ntypedef struct duss_mb_filter_t duss_mb_filter_t;\n\ntypedef struct duss_mb_route_table_t duss_mb_route_table_t;\n\ntypedef struct fd_set fd_set;\n\ntypedef struct duss_osal_msgq_attrib_t duss_osal_msgq_attrib_t;\n\ntypedef struct duss_osal_event_attrib_t duss_osal_event_attrib_t;\n\ntypedef uint8_t duss_hal_state_t;\n\ntypedef uint8_t duss_hal_class_t;\n\ntypedef void (*duss_osal_task_entry_t)(void *);\n\ntypedef struct duss_hal_storage_params duss_hal_storage_params;\n\ntypedef struct duss_hal_storage_info duss_hal_storage_info;\n\ntypedef struct _DUSS_MSG_RACING_CHANNEL_OCCUPIED_IPSD_t _DUSS_MSG_RACING_CHANNEL_OCCUPIED_IPSD_t;\n\ntypedef struct _DUSS_MSG_RACING_CHANNEL_OCCUPIED_IPSD_t DUSS_MSG_RACING_CHANNEL_OCCUPIED_IPSD_t;\n\ntypedef struct anon_struct_conflict47e0 anon_struct_conflict47e0;\n\ntypedef struct rc_set_subtrim_t rc_set_subtrim_t;\n\ntypedef struct rc_set_endpoint_t rc_set_endpoint_t;\n\ntypedef struct rc_set_subtrim_reverse_t rc_set_subtrim_reverse_t;\n\ntypedef struct _DUSS_MSG_CAM_STATE_t _DUSS_MSG_CAM_STATE_t;\n\ntypedef struct _DUSS_MSG_CAM_STATE_t DUSS_MSG_CAM_STATE_t;\n\ntypedef union anon_union_conflict23e3_for_u anon_union_conflict23e3_for_u;\n\ntypedef union anon_union_conflict2544_for_u anon_union_conflict2544_for_u;\n\ntypedef enum gs_video_channel_message_id_t\n{\n    GS_CHANNEL_MSG_HDMI_PLUG = 0,\n    GS_CHANNEL_MSG_CAMERA_WORKMODE = 1,\n    GS_CHANNEL_MSG_CAMERA_PBMODE = 2,\n    GS_CHANNEL_MSG_CAMERA_MODEL = 3,\n    GS_CHANNEL_MSG_WIRELESS_LINK_STATUS = 4,\n    GS_CHANNEL_MSG_LOCAL_PLAYBACK_ON = 5,\n    GS_CHANNEL_MSG_LOCAL_PLAYBACK_OFF = 6,\n    GS_CHANNEL_MSG_CHANNEL_ENABLE = 7,\n    GS_CHANNEL_MSG_CHANNEL_DISABLE = 8,\n    GS_CHANNEL_MSG_SUB_CHANNEL_ENABLE = 9,\n    GS_CHANNEL_MSG_SUB_CHANNEL_DISABLE = 10,\n    GS_CHANNEL_MSG_ANALOG_VIDEO_ON = 11,\n    GS_CHANNEL_MSG_ANALOG_VIDEO_OFF = 12,\n    GS_CHANNEL_MSG_RC_SETTING_ON = 13,\n    GS_CHANNEL_MSG_RC_SETTING_OFF = 14,\n    GS_CHANNEL_MSG_ID_COUNT = 15\n} gs_video_channel_message_id_t;\n\ntypedef union anon_union_conflict3b31_for_field_1 anon_union_conflict3b31_for_field_1;\n\ntypedef enum gs_sub_channel_id\n{\n    GS_FIRST_SUB_CHANNEL = 0,\n    GS_LIVEVIEW_SUB_CHANNEL = 0,\n    GS_PLAYBACK_SUB_CHANNEL = 1,\n    GS_RACING_SUB_CHANNEL = 2,\n    GS_HDMI_SUB_CHANNEL = 3,\n    GS_ANALOG_VIDEO_SUB_CHANNEL = 4,\n    GS_RC_SETTING_SUB_CHANNEL = 5,\n    GS_SUB_CHANNEL_COUNT = 6,\n    GS_NO_SUB_CHANNEL = 7\n} gs_sub_channel_id;\n\ntypedef enum gs_sub_channel_id gs_sub_channel_id_t;\n\ntypedef struct AVClass AVClass;\n\ntypedef enum AVClassCategory\n{\n    AV_CLASS_CATEGORY_NA = 0,\n    AV_CLASS_CATEGORY_INPUT = 1,\n    AV_CLASS_CATEGORY_OUTPUT = 2,\n    AV_CLASS_CATEGORY_MUXER = 3,\n    AV_CLASS_CATEGORY_DEMUXER = 4,\n    AV_CLASS_CATEGORY_ENCODER = 5,\n    AV_CLASS_CATEGORY_DECODER = 6,\n    AV_CLASS_CATEGORY_FILTER = 7,\n    AV_CLASS_CATEGORY_BITSTREAM_FILTER = 8,\n    AV_CLASS_CATEGORY_SWSCALER = 9,\n    AV_CLASS_CATEGORY_SWRESAMPLER = 10,\n    AV_CLASS_CATEGORY_DEVICE_VIDEO_OUTPUT = 40,\n    AV_CLASS_CATEGORY_DEVICE_VIDEO_INPUT = 41,\n    AV_CLASS_CATEGORY_DEVICE_AUDIO_OUTPUT = 42,\n    AV_CLASS_CATEGORY_DEVICE_AUDIO_INPUT = 43,\n    AV_CLASS_CATEGORY_DEVICE_OUTPUT = 44,\n    AV_CLASS_CATEGORY_DEVICE_INPUT = 45,\n    AV_CLASS_CATEGORY_NB = 46\n} AVClassCategory;\n\ntypedef struct AVOptionRanges AVOptionRanges;\n\ntypedef struct AVInputFormat AVInputFormat;\n\ntypedef struct AVProbeData AVProbeData;\n\ntypedef struct AVPacket AVPacket;\n\ntypedef struct AVDeviceInfoList AVDeviceInfoList;\n\ntypedef struct AVDeviceCapabilitiesQuery AVDeviceCapabilitiesQuery;\n\ntypedef struct AVOutputFormat AVOutputFormat;\n\ntypedef enum AVCodecID\n{\n    AV_CODEC_ID_NONE = 0,\n    CODEC_ID_NONE = 0,\n    AV_CODEC_ID_MPEG1VIDEO = 1,\n    CODEC_ID_MPEG1VIDEO = 1,\n    AV_CODEC_ID_MPEG2VIDEO = 2,\n    CODEC_ID_MPEG2VIDEO = 2,\n    AV_CODEC_ID_MPEG2VIDEO_XVMC = 3,\n    CODEC_ID_MPEG2VIDEO_XVMC = 3,\n    AV_CODEC_ID_H261 = 4,\n    CODEC_ID_H261 = 4,\n    AV_CODEC_ID_H263 = 5,\n    CODEC_ID_H263 = 5,\n    AV_CODEC_ID_RV10 = 6,\n    CODEC_ID_RV10 = 6,\n    AV_CODEC_ID_RV20 = 7,\n    CODEC_ID_RV20 = 7,\n    AV_CODEC_ID_MJPEG = 8,\n    CODEC_ID_MJPEG = 8,\n    AV_CODEC_ID_MJPEGB = 9,\n    CODEC_ID_MJPEGB = 9,\n    AV_CODEC_ID_LJPEG = 10,\n    CODEC_ID_LJPEG = 10,\n    AV_CODEC_ID_SP5X = 11,\n    CODEC_ID_SP5X = 11,\n    AV_CODEC_ID_JPEGLS = 12,\n    CODEC_ID_JPEGLS = 12,\n    AV_CODEC_ID_MPEG4 = 13,\n    CODEC_ID_MPEG4 = 13,\n    AV_CODEC_ID_RAWVIDEO = 14,\n    CODEC_ID_RAWVIDEO = 14,\n    AV_CODEC_ID_MSMPEG4V1 = 15,\n    CODEC_ID_MSMPEG4V1 = 15,\n    AV_CODEC_ID_MSMPEG4V2 = 16,\n    CODEC_ID_MSMPEG4V2 = 16,\n    AV_CODEC_ID_MSMPEG4V3 = 17,\n    CODEC_ID_MSMPEG4V3 = 17,\n    AV_CODEC_ID_WMV1 = 18,\n    CODEC_ID_WMV1 = 18,\n    AV_CODEC_ID_WMV2 = 19,\n    CODEC_ID_WMV2 = 19,\n    AV_CODEC_ID_H263P = 20,\n    CODEC_ID_H263P = 20,\n    AV_CODEC_ID_H263I = 21,\n    CODEC_ID_H263I = 21,\n    AV_CODEC_ID_FLV1 = 22,\n    CODEC_ID_FLV1 = 22,\n    AV_CODEC_ID_SVQ1 = 23,\n    CODEC_ID_SVQ1 = 23,\n    AV_CODEC_ID_SVQ3 = 24,\n    CODEC_ID_SVQ3 = 24,\n    AV_CODEC_ID_DVVIDEO = 25,\n    CODEC_ID_DVVIDEO = 25,\n    AV_CODEC_ID_HUFFYUV = 26,\n    CODEC_ID_HUFFYUV = 26,\n    AV_CODEC_ID_CYUV = 27,\n    CODEC_ID_CYUV = 27,\n    AV_CODEC_ID_H264 = 28,\n    CODEC_ID_H264 = 28,\n    AV_CODEC_ID_INDEO3 = 29,\n    CODEC_ID_INDEO3 = 29,\n    AV_CODEC_ID_VP3 = 30,\n    CODEC_ID_VP3 = 30,\n    AV_CODEC_ID_THEORA = 31,\n    CODEC_ID_THEORA = 31,\n    AV_CODEC_ID_ASV1 = 32,\n    CODEC_ID_ASV1 = 32,\n    AV_CODEC_ID_ASV2 = 33,\n    CODEC_ID_ASV2 = 33,\n    AV_CODEC_ID_FFV1 = 34,\n    CODEC_ID_FFV1 = 34,\n    AV_CODEC_ID_4XM = 35,\n    CODEC_ID_4XM = 35,\n    AV_CODEC_ID_VCR1 = 36,\n    CODEC_ID_VCR1 = 36,\n    AV_CODEC_ID_CLJR = 37,\n    CODEC_ID_CLJR = 37,\n    AV_CODEC_ID_MDEC = 38,\n    CODEC_ID_MDEC = 38,\n    AV_CODEC_ID_ROQ = 39,\n    CODEC_ID_ROQ = 39,\n    AV_CODEC_ID_INTERPLAY_VIDEO = 40,\n    CODEC_ID_INTERPLAY_VIDEO = 40,\n    AV_CODEC_ID_XAN_WC3 = 41,\n    CODEC_ID_XAN_WC3 = 41,\n    AV_CODEC_ID_XAN_WC4 = 42,\n    CODEC_ID_XAN_WC4 = 42,\n    AV_CODEC_ID_RPZA = 43,\n    CODEC_ID_RPZA = 43,\n    AV_CODEC_ID_CINEPAK = 44,\n    CODEC_ID_CINEPAK = 44,\n    AV_CODEC_ID_WS_VQA = 45,\n    CODEC_ID_WS_VQA = 45,\n    AV_CODEC_ID_MSRLE = 46,\n    CODEC_ID_MSRLE = 46,\n    AV_CODEC_ID_MSVIDEO1 = 47,\n    CODEC_ID_MSVIDEO1 = 47,\n    AV_CODEC_ID_IDCIN = 48,\n    CODEC_ID_IDCIN = 48,\n    AV_CODEC_ID_8BPS = 49,\n    CODEC_ID_8BPS = 49,\n    AV_CODEC_ID_SMC = 50,\n    CODEC_ID_SMC = 50,\n    AV_CODEC_ID_FLIC = 51,\n    CODEC_ID_FLIC = 51,\n    AV_CODEC_ID_TRUEMOTION1 = 52,\n    CODEC_ID_TRUEMOTION1 = 52,\n    AV_CODEC_ID_VMDVIDEO = 53,\n    CODEC_ID_VMDVIDEO = 53,\n    AV_CODEC_ID_MSZH = 54,\n    CODEC_ID_MSZH = 54,\n    AV_CODEC_ID_ZLIB = 55,\n    CODEC_ID_ZLIB = 55,\n    AV_CODEC_ID_QTRLE = 56,\n    CODEC_ID_QTRLE = 56,\n    AV_CODEC_ID_TSCC = 57,\n    CODEC_ID_TSCC = 57,\n    AV_CODEC_ID_ULTI = 58,\n    CODEC_ID_ULTI = 58,\n    AV_CODEC_ID_QDRAW = 59,\n    CODEC_ID_QDRAW = 59,\n    AV_CODEC_ID_VIXL = 60,\n    CODEC_ID_VIXL = 60,\n    AV_CODEC_ID_QPEG = 61,\n    CODEC_ID_QPEG = 61,\n    AV_CODEC_ID_PNG = 62,\n    CODEC_ID_PNG = 62,\n    AV_CODEC_ID_PPM = 63,\n    CODEC_ID_PPM = 63,\n    AV_CODEC_ID_PBM = 64,\n    CODEC_ID_PBM = 64,\n    AV_CODEC_ID_PGM = 65,\n    CODEC_ID_PGM = 65,\n    AV_CODEC_ID_PGMYUV = 66,\n    CODEC_ID_PGMYUV = 66,\n    AV_CODEC_ID_PAM = 67,\n    CODEC_ID_PAM = 67,\n    AV_CODEC_ID_FFVHUFF = 68,\n    CODEC_ID_FFVHUFF = 68,\n    AV_CODEC_ID_RV30 = 69,\n    CODEC_ID_RV30 = 69,\n    AV_CODEC_ID_RV40 = 70,\n    CODEC_ID_RV40 = 70,\n    AV_CODEC_ID_VC1 = 71,\n    CODEC_ID_VC1 = 71,\n    AV_CODEC_ID_WMV3 = 72,\n    CODEC_ID_WMV3 = 72,\n    AV_CODEC_ID_LOCO = 73,\n    CODEC_ID_LOCO = 73,\n    AV_CODEC_ID_WNV1 = 74,\n    CODEC_ID_WNV1 = 74,\n    AV_CODEC_ID_AASC = 75,\n    CODEC_ID_AASC = 75,\n    AV_CODEC_ID_INDEO2 = 76,\n    CODEC_ID_INDEO2 = 76,\n    AV_CODEC_ID_FRAPS = 77,\n    CODEC_ID_FRAPS = 77,\n    AV_CODEC_ID_TRUEMOTION2 = 78,\n    CODEC_ID_TRUEMOTION2 = 78,\n    AV_CODEC_ID_BMP = 79,\n    CODEC_ID_BMP = 79,\n    AV_CODEC_ID_CSCD = 80,\n    CODEC_ID_CSCD = 80,\n    AV_CODEC_ID_MMVIDEO = 81,\n    CODEC_ID_MMVIDEO = 81,\n    AV_CODEC_ID_ZMBV = 82,\n    CODEC_ID_ZMBV = 82,\n    AV_CODEC_ID_AVS = 83,\n    CODEC_ID_AVS = 83,\n    AV_CODEC_ID_SMACKVIDEO = 84,\n    CODEC_ID_SMACKVIDEO = 84,\n    AV_CODEC_ID_NUV = 85,\n    CODEC_ID_NUV = 85,\n    AV_CODEC_ID_KMVC = 86,\n    CODEC_ID_KMVC = 86,\n    AV_CODEC_ID_FLASHSV = 87,\n    CODEC_ID_FLASHSV = 87,\n    AV_CODEC_ID_CAVS = 88,\n    CODEC_ID_CAVS = 88,\n    AV_CODEC_ID_JPEG2000 = 89,\n    CODEC_ID_JPEG2000 = 89,\n    AV_CODEC_ID_VMNC = 90,\n    CODEC_ID_VMNC = 90,\n    AV_CODEC_ID_VP5 = 91,\n    CODEC_ID_VP5 = 91,\n    AV_CODEC_ID_VP6 = 92,\n    CODEC_ID_VP6 = 92,\n    AV_CODEC_ID_VP6F = 93,\n    CODEC_ID_VP6F = 93,\n    AV_CODEC_ID_TARGA = 94,\n    CODEC_ID_TARGA = 94,\n    AV_CODEC_ID_DSICINVIDEO = 95,\n    CODEC_ID_DSICINVIDEO = 95,\n    AV_CODEC_ID_TIERTEXSEQVIDEO = 96,\n    CODEC_ID_TIERTEXSEQVIDEO = 96,\n    AV_CODEC_ID_TIFF = 97,\n    CODEC_ID_TIFF = 97,\n    AV_CODEC_ID_GIF = 98,\n    CODEC_ID_GIF = 98,\n    AV_CODEC_ID_DXA = 99,\n    CODEC_ID_DXA = 99,\n    AV_CODEC_ID_DNXHD = 100,\n    CODEC_ID_DNXHD = 100,\n    AV_CODEC_ID_THP = 101,\n    CODEC_ID_THP = 101,\n    AV_CODEC_ID_SGI = 102,\n    CODEC_ID_SGI = 102,\n    AV_CODEC_ID_C93 = 103,\n    CODEC_ID_C93 = 103,\n    AV_CODEC_ID_BETHSOFTVID = 104,\n    CODEC_ID_BETHSOFTVID = 104,\n    AV_CODEC_ID_PTX = 105,\n    CODEC_ID_PTX = 105,\n    AV_CODEC_ID_TXD = 106,\n    CODEC_ID_TXD = 106,\n    AV_CODEC_ID_VP6A = 107,\n    CODEC_ID_VP6A = 107,\n    AV_CODEC_ID_AMV = 108,\n    CODEC_ID_AMV = 108,\n    AV_CODEC_ID_VB = 109,\n    CODEC_ID_VB = 109,\n    AV_CODEC_ID_PCX = 110,\n    CODEC_ID_PCX = 110,\n    AV_CODEC_ID_SUNRAST = 111,\n    CODEC_ID_SUNRAST = 111,\n    AV_CODEC_ID_INDEO4 = 112,\n    CODEC_ID_INDEO4 = 112,\n    AV_CODEC_ID_INDEO5 = 113,\n    CODEC_ID_INDEO5 = 113,\n    AV_CODEC_ID_MIMIC = 114,\n    CODEC_ID_MIMIC = 114,\n    AV_CODEC_ID_RL2 = 115,\n    CODEC_ID_RL2 = 115,\n    AV_CODEC_ID_ESCAPE124 = 116,\n    CODEC_ID_ESCAPE124 = 116,\n    AV_CODEC_ID_DIRAC = 117,\n    CODEC_ID_DIRAC = 117,\n    AV_CODEC_ID_BFI = 118,\n    CODEC_ID_BFI = 118,\n    AV_CODEC_ID_CMV = 119,\n    CODEC_ID_CMV = 119,\n    AV_CODEC_ID_MOTIONPIXELS = 120,\n    CODEC_ID_MOTIONPIXELS = 120,\n    AV_CODEC_ID_TGV = 121,\n    CODEC_ID_TGV = 121,\n    AV_CODEC_ID_TGQ = 122,\n    CODEC_ID_TGQ = 122,\n    AV_CODEC_ID_TQI = 123,\n    CODEC_ID_TQI = 123,\n    AV_CODEC_ID_AURA = 124,\n    CODEC_ID_AURA = 124,\n    AV_CODEC_ID_AURA2 = 125,\n    CODEC_ID_AURA2 = 125,\n    AV_CODEC_ID_V210X = 126,\n    CODEC_ID_V210X = 126,\n    AV_CODEC_ID_TMV = 127,\n    CODEC_ID_TMV = 127,\n    AV_CODEC_ID_V210 = 128,\n    CODEC_ID_V210 = 128,\n    AV_CODEC_ID_DPX = 129,\n    CODEC_ID_DPX = 129,\n    AV_CODEC_ID_MAD = 130,\n    CODEC_ID_MAD = 130,\n    AV_CODEC_ID_FRWU = 131,\n    CODEC_ID_FRWU = 131,\n    AV_CODEC_ID_FLASHSV2 = 132,\n    CODEC_ID_FLASHSV2 = 132,\n    AV_CODEC_ID_CDGRAPHICS = 133,\n    CODEC_ID_CDGRAPHICS = 133,\n    AV_CODEC_ID_R210 = 134,\n    CODEC_ID_R210 = 134,\n    AV_CODEC_ID_ANM = 135,\n    CODEC_ID_ANM = 135,\n    AV_CODEC_ID_BINKVIDEO = 136,\n    CODEC_ID_BINKVIDEO = 136,\n    AV_CODEC_ID_IFF_ILBM = 137,\n    CODEC_ID_IFF_ILBM = 137,\n    AV_CODEC_ID_IFF_BYTERUN1 = 138,\n    CODEC_ID_IFF_BYTERUN1 = 138,\n    AV_CODEC_ID_KGV1 = 139,\n    CODEC_ID_KGV1 = 139,\n    AV_CODEC_ID_YOP = 140,\n    CODEC_ID_YOP = 140,\n    AV_CODEC_ID_VP8 = 141,\n    CODEC_ID_VP8 = 141,\n    AV_CODEC_ID_PICTOR = 142,\n    CODEC_ID_PICTOR = 142,\n    AV_CODEC_ID_ANSI = 143,\n    CODEC_ID_ANSI = 143,\n    AV_CODEC_ID_A64_MULTI = 144,\n    CODEC_ID_A64_MULTI = 144,\n    AV_CODEC_ID_A64_MULTI5 = 145,\n    CODEC_ID_A64_MULTI5 = 145,\n    AV_CODEC_ID_R10K = 146,\n    CODEC_ID_R10K = 146,\n    AV_CODEC_ID_MXPEG = 147,\n    CODEC_ID_MXPEG = 147,\n    AV_CODEC_ID_LAGARITH = 148,\n    CODEC_ID_LAGARITH = 148,\n    AV_CODEC_ID_PRORES = 149,\n    CODEC_ID_PRORES = 149,\n    AV_CODEC_ID_JV = 150,\n    CODEC_ID_JV = 150,\n    AV_CODEC_ID_DFA = 151,\n    CODEC_ID_DFA = 151,\n    AV_CODEC_ID_WMV3IMAGE = 152,\n    CODEC_ID_WMV3IMAGE = 152,\n    AV_CODEC_ID_VC1IMAGE = 153,\n    CODEC_ID_VC1IMAGE = 153,\n    AV_CODEC_ID_UTVIDEO = 154,\n    CODEC_ID_UTVIDEO = 154,\n    AV_CODEC_ID_BMV_VIDEO = 155,\n    CODEC_ID_BMV_VIDEO = 155,\n    AV_CODEC_ID_VBLE = 156,\n    CODEC_ID_VBLE = 156,\n    AV_CODEC_ID_DXTORY = 157,\n    CODEC_ID_DXTORY = 157,\n    AV_CODEC_ID_V410 = 158,\n    CODEC_ID_V410 = 158,\n    AV_CODEC_ID_XWD = 159,\n    CODEC_ID_XWD = 159,\n    AV_CODEC_ID_CDXL = 160,\n    CODEC_ID_CDXL = 160,\n    AV_CODEC_ID_XBM = 161,\n    CODEC_ID_XBM = 161,\n    AV_CODEC_ID_ZEROCODEC = 162,\n    CODEC_ID_ZEROCODEC = 162,\n    AV_CODEC_ID_MSS1 = 163,\n    CODEC_ID_MSS1 = 163,\n    AV_CODEC_ID_MSA1 = 164,\n    CODEC_ID_MSA1 = 164,\n    AV_CODEC_ID_TSCC2 = 165,\n    CODEC_ID_TSCC2 = 165,\n    AV_CODEC_ID_MTS2 = 166,\n    CODEC_ID_MTS2 = 166,\n    AV_CODEC_ID_CLLC = 167,\n    CODEC_ID_CLLC = 167,\n    AV_CODEC_ID_MSS2 = 168,\n    AV_CODEC_ID_VP9 = 169,\n    AV_CODEC_ID_AIC = 170,\n    AV_CODEC_ID_ESCAPE130_DEPRECATED = 171,\n    AV_CODEC_ID_G2M_DEPRECATED = 172,\n    AV_CODEC_ID_WEBP_DEPRECATED = 173,\n    AV_CODEC_ID_HNM4_VIDEO = 174,\n    AV_CODEC_ID_HEVC_DEPRECATED = 175,\n    AV_CODEC_ID_FIC = 176,\n    AV_CODEC_ID_ALIAS_PIX = 177,\n    AV_CODEC_ID_BRENDER_PIX_DEPRECATED = 178,\n    AV_CODEC_ID_PAF_VIDEO_DEPRECATED = 179,\n    AV_CODEC_ID_EXR_DEPRECATED = 180,\n    AV_CODEC_ID_VP7_DEPRECATED = 181,\n    AV_CODEC_ID_SANM_DEPRECATED = 182,\n    AV_CODEC_ID_SGIRLE_DEPRECATED = 183,\n    AV_CODEC_ID_MVC1_DEPRECATED = 184,\n    AV_CODEC_ID_MVC2_DEPRECATED = 185,\n    AV_CODEC_ID_FIRST_AUDIO = 65536,\n    AV_CODEC_ID_PCM_S16LE = 65536,\n    CODEC_ID_FIRST_AUDIO = 65536,\n    CODEC_ID_PCM_S16LE = 65536,\n    AV_CODEC_ID_PCM_S16BE = 65537,\n    CODEC_ID_PCM_S16BE = 65537,\n    AV_CODEC_ID_PCM_U16LE = 65538,\n    CODEC_ID_PCM_U16LE = 65538,\n    AV_CODEC_ID_PCM_U16BE = 65539,\n    CODEC_ID_PCM_U16BE = 65539,\n    AV_CODEC_ID_PCM_S8 = 65540,\n    CODEC_ID_PCM_S8 = 65540,\n    AV_CODEC_ID_PCM_U8 = 65541,\n    CODEC_ID_PCM_U8 = 65541,\n    AV_CODEC_ID_PCM_MULAW = 65542,\n    CODEC_ID_PCM_MULAW = 65542,\n    AV_CODEC_ID_PCM_ALAW = 65543,\n    CODEC_ID_PCM_ALAW = 65543,\n    AV_CODEC_ID_PCM_S32LE = 65544,\n    CODEC_ID_PCM_S32LE = 65544,\n    AV_CODEC_ID_PCM_S32BE = 65545,\n    CODEC_ID_PCM_S32BE = 65545,\n    AV_CODEC_ID_PCM_U32LE = 65546,\n    CODEC_ID_PCM_U32LE = 65546,\n    AV_CODEC_ID_PCM_U32BE = 65547,\n    CODEC_ID_PCM_U32BE = 65547,\n    AV_CODEC_ID_PCM_S24LE = 65548,\n    CODEC_ID_PCM_S24LE = 65548,\n    AV_CODEC_ID_PCM_S24BE = 65549,\n    CODEC_ID_PCM_S24BE = 65549,\n    AV_CODEC_ID_PCM_U24LE = 65550,\n    CODEC_ID_PCM_U24LE = 65550,\n    AV_CODEC_ID_PCM_U24BE = 65551,\n    CODEC_ID_PCM_U24BE = 65551,\n    AV_CODEC_ID_PCM_S24DAUD = 65552,\n    CODEC_ID_PCM_S24DAUD = 65552,\n    AV_CODEC_ID_PCM_ZORK = 65553,\n    CODEC_ID_PCM_ZORK = 65553,\n    AV_CODEC_ID_PCM_S16LE_PLANAR = 65554,\n    CODEC_ID_PCM_S16LE_PLANAR = 65554,\n    AV_CODEC_ID_PCM_DVD = 65555,\n    CODEC_ID_PCM_DVD = 65555,\n    AV_CODEC_ID_PCM_F32BE = 65556,\n    CODEC_ID_PCM_F32BE = 65556,\n    AV_CODEC_ID_PCM_F32LE = 65557,\n    CODEC_ID_PCM_F32LE = 65557,\n    AV_CODEC_ID_PCM_F64BE = 65558,\n    CODEC_ID_PCM_F64BE = 65558,\n    AV_CODEC_ID_PCM_F64LE = 65559,\n    CODEC_ID_PCM_F64LE = 65559,\n    AV_CODEC_ID_PCM_BLURAY = 65560,\n    CODEC_ID_PCM_BLURAY = 65560,\n    AV_CODEC_ID_PCM_LXF = 65561,\n    CODEC_ID_PCM_LXF = 65561,\n    AV_CODEC_ID_S302M = 65562,\n    CODEC_ID_S302M = 65562,\n    AV_CODEC_ID_PCM_S8_PLANAR = 65563,\n    CODEC_ID_PCM_S8_PLANAR = 65563,\n    AV_CODEC_ID_PCM_S24LE_PLANAR_DEPRECATED = 65564,\n    AV_CODEC_ID_PCM_S32LE_PLANAR_DEPRECATED = 65565,\n    AV_CODEC_ID_ADPCM_IMA_QT = 69632,\n    CODEC_ID_ADPCM_IMA_QT = 69632,\n    AV_CODEC_ID_ADPCM_IMA_WAV = 69633,\n    CODEC_ID_ADPCM_IMA_WAV = 69633,\n    AV_CODEC_ID_ADPCM_IMA_DK3 = 69634,\n    CODEC_ID_ADPCM_IMA_DK3 = 69634,\n    AV_CODEC_ID_ADPCM_IMA_DK4 = 69635,\n    CODEC_ID_ADPCM_IMA_DK4 = 69635,\n    AV_CODEC_ID_ADPCM_IMA_WS = 69636,\n    CODEC_ID_ADPCM_IMA_WS = 69636,\n    AV_CODEC_ID_ADPCM_IMA_SMJPEG = 69637,\n    CODEC_ID_ADPCM_IMA_SMJPEG = 69637,\n    AV_CODEC_ID_ADPCM_MS = 69638,\n    CODEC_ID_ADPCM_MS = 69638,\n    AV_CODEC_ID_ADPCM_4XM = 69639,\n    CODEC_ID_ADPCM_4XM = 69639,\n    AV_CODEC_ID_ADPCM_XA = 69640,\n    CODEC_ID_ADPCM_XA = 69640,\n    AV_CODEC_ID_ADPCM_ADX = 69641,\n    CODEC_ID_ADPCM_ADX = 69641,\n    AV_CODEC_ID_ADPCM_EA = 69642,\n    CODEC_ID_ADPCM_EA = 69642,\n    AV_CODEC_ID_ADPCM_G726 = 69643,\n    CODEC_ID_ADPCM_G726 = 69643,\n    AV_CODEC_ID_ADPCM_CT = 69644,\n    CODEC_ID_ADPCM_CT = 69644,\n    AV_CODEC_ID_ADPCM_SWF = 69645,\n    CODEC_ID_ADPCM_SWF = 69645,\n    AV_CODEC_ID_ADPCM_YAMAHA = 69646,\n    CODEC_ID_ADPCM_YAMAHA = 69646,\n    AV_CODEC_ID_ADPCM_SBPRO_4 = 69647,\n    CODEC_ID_ADPCM_SBPRO_4 = 69647,\n    AV_CODEC_ID_ADPCM_SBPRO_3 = 69648,\n    CODEC_ID_ADPCM_SBPRO_3 = 69648,\n    AV_CODEC_ID_ADPCM_SBPRO_2 = 69649,\n    CODEC_ID_ADPCM_SBPRO_2 = 69649,\n    AV_CODEC_ID_ADPCM_THP = 69650,\n    CODEC_ID_ADPCM_THP = 69650,\n    AV_CODEC_ID_ADPCM_IMA_AMV = 69651,\n    CODEC_ID_ADPCM_IMA_AMV = 69651,\n    AV_CODEC_ID_ADPCM_EA_R1 = 69652,\n    CODEC_ID_ADPCM_EA_R1 = 69652,\n    AV_CODEC_ID_ADPCM_EA_R3 = 69653,\n    CODEC_ID_ADPCM_EA_R3 = 69653,\n    AV_CODEC_ID_ADPCM_EA_R2 = 69654,\n    CODEC_ID_ADPCM_EA_R2 = 69654,\n    AV_CODEC_ID_ADPCM_IMA_EA_SEAD = 69655,\n    CODEC_ID_ADPCM_IMA_EA_SEAD = 69655,\n    AV_CODEC_ID_ADPCM_IMA_EA_EACS = 69656,\n    CODEC_ID_ADPCM_IMA_EA_EACS = 69656,\n    AV_CODEC_ID_ADPCM_EA_XAS = 69657,\n    CODEC_ID_ADPCM_EA_XAS = 69657,\n    AV_CODEC_ID_ADPCM_EA_MAXIS_XA = 69658,\n    CODEC_ID_ADPCM_EA_MAXIS_XA = 69658,\n    AV_CODEC_ID_ADPCM_IMA_ISS = 69659,\n    CODEC_ID_ADPCM_IMA_ISS = 69659,\n    AV_CODEC_ID_ADPCM_G722 = 69660,\n    CODEC_ID_ADPCM_G722 = 69660,\n    AV_CODEC_ID_ADPCM_IMA_APC = 69661,\n    CODEC_ID_ADPCM_IMA_APC = 69661,\n    AV_CODEC_ID_ADPCM_VIMA_DEPRECATED = 69662,\n    AV_CODEC_ID_AMR_NB = 73728,\n    CODEC_ID_AMR_NB = 73728,\n    AV_CODEC_ID_AMR_WB = 73729,\n    CODEC_ID_AMR_WB = 73729,\n    AV_CODEC_ID_RA_144 = 77824,\n    CODEC_ID_RA_144 = 77824,\n    AV_CODEC_ID_RA_288 = 77825,\n    CODEC_ID_RA_288 = 77825,\n    AV_CODEC_ID_ROQ_DPCM = 81920,\n    CODEC_ID_ROQ_DPCM = 81920,\n    AV_CODEC_ID_INTERPLAY_DPCM = 81921,\n    CODEC_ID_INTERPLAY_DPCM = 81921,\n    AV_CODEC_ID_XAN_DPCM = 81922,\n    CODEC_ID_XAN_DPCM = 81922,\n    AV_CODEC_ID_SOL_DPCM = 81923,\n    CODEC_ID_SOL_DPCM = 81923,\n    AV_CODEC_ID_MP2 = 86016,\n    CODEC_ID_MP2 = 86016,\n    AV_CODEC_ID_MP3 = 86017,\n    CODEC_ID_MP3 = 86017,\n    AV_CODEC_ID_AAC = 86018,\n    CODEC_ID_AAC = 86018,\n    AV_CODEC_ID_AC3 = 86019,\n    CODEC_ID_AC3 = 86019,\n    AV_CODEC_ID_DTS = 86020,\n    CODEC_ID_DTS = 86020,\n    AV_CODEC_ID_VORBIS = 86021,\n    CODEC_ID_VORBIS = 86021,\n    AV_CODEC_ID_DVAUDIO = 86022,\n    CODEC_ID_DVAUDIO = 86022,\n    AV_CODEC_ID_WMAV1 = 86023,\n    CODEC_ID_WMAV1 = 86023,\n    AV_CODEC_ID_WMAV2 = 86024,\n    CODEC_ID_WMAV2 = 86024,\n    AV_CODEC_ID_MACE3 = 86025,\n    CODEC_ID_MACE3 = 86025,\n    AV_CODEC_ID_MACE6 = 86026,\n    CODEC_ID_MACE6 = 86026,\n    AV_CODEC_ID_VMDAUDIO = 86027,\n    CODEC_ID_VMDAUDIO = 86027,\n    AV_CODEC_ID_FLAC = 86028,\n    CODEC_ID_FLAC = 86028,\n    AV_CODEC_ID_MP3ADU = 86029,\n    CODEC_ID_MP3ADU = 86029,\n    AV_CODEC_ID_MP3ON4 = 86030,\n    CODEC_ID_MP3ON4 = 86030,\n    AV_CODEC_ID_SHORTEN = 86031,\n    CODEC_ID_SHORTEN = 86031,\n    AV_CODEC_ID_ALAC = 86032,\n    CODEC_ID_ALAC = 86032,\n    AV_CODEC_ID_WESTWOOD_SND1 = 86033,\n    CODEC_ID_WESTWOOD_SND1 = 86033,\n    AV_CODEC_ID_GSM = 86034,\n    CODEC_ID_GSM = 86034,\n    AV_CODEC_ID_QDM2 = 86035,\n    CODEC_ID_QDM2 = 86035,\n    AV_CODEC_ID_COOK = 86036,\n    CODEC_ID_COOK = 86036,\n    AV_CODEC_ID_TRUESPEECH = 86037,\n    CODEC_ID_TRUESPEECH = 86037,\n    AV_CODEC_ID_TTA = 86038,\n    CODEC_ID_TTA = 86038,\n    AV_CODEC_ID_SMACKAUDIO = 86039,\n    CODEC_ID_SMACKAUDIO = 86039,\n    AV_CODEC_ID_QCELP = 86040,\n    CODEC_ID_QCELP = 86040,\n    AV_CODEC_ID_WAVPACK = 86041,\n    CODEC_ID_WAVPACK = 86041,\n    AV_CODEC_ID_DSICINAUDIO = 86042,\n    CODEC_ID_DSICINAUDIO = 86042,\n    AV_CODEC_ID_IMC = 86043,\n    CODEC_ID_IMC = 86043,\n    AV_CODEC_ID_MUSEPACK7 = 86044,\n    CODEC_ID_MUSEPACK7 = 86044,\n    AV_CODEC_ID_MLP = 86045,\n    CODEC_ID_MLP = 86045,\n    AV_CODEC_ID_GSM_MS = 86046,\n    CODEC_ID_GSM_MS = 86046,\n    AV_CODEC_ID_ATRAC3 = 86047,\n    CODEC_ID_ATRAC3 = 86047,\n    AV_CODEC_ID_VOXWARE = 86048,\n    CODEC_ID_VOXWARE = 86048,\n    AV_CODEC_ID_APE = 86049,\n    CODEC_ID_APE = 86049,\n    AV_CODEC_ID_NELLYMOSER = 86050,\n    CODEC_ID_NELLYMOSER = 86050,\n    AV_CODEC_ID_MUSEPACK8 = 86051,\n    CODEC_ID_MUSEPACK8 = 86051,\n    AV_CODEC_ID_SPEEX = 86052,\n    CODEC_ID_SPEEX = 86052,\n    AV_CODEC_ID_WMAVOICE = 86053,\n    CODEC_ID_WMAVOICE = 86053,\n    AV_CODEC_ID_WMAPRO = 86054,\n    CODEC_ID_WMAPRO = 86054,\n    AV_CODEC_ID_WMALOSSLESS = 86055,\n    CODEC_ID_WMALOSSLESS = 86055,\n    AV_CODEC_ID_ATRAC3P = 86056,\n    CODEC_ID_ATRAC3P = 86056,\n    AV_CODEC_ID_EAC3 = 86057,\n    CODEC_ID_EAC3 = 86057,\n    AV_CODEC_ID_SIPR = 86058,\n    CODEC_ID_SIPR = 86058,\n    AV_CODEC_ID_MP1 = 86059,\n    CODEC_ID_MP1 = 86059,\n    AV_CODEC_ID_TWINVQ = 86060,\n    CODEC_ID_TWINVQ = 86060,\n    AV_CODEC_ID_TRUEHD = 86061,\n    CODEC_ID_TRUEHD = 86061,\n    AV_CODEC_ID_MP4ALS = 86062,\n    CODEC_ID_MP4ALS = 86062,\n    AV_CODEC_ID_ATRAC1 = 86063,\n    CODEC_ID_ATRAC1 = 86063,\n    AV_CODEC_ID_BINKAUDIO_RDFT = 86064,\n    CODEC_ID_BINKAUDIO_RDFT = 86064,\n    AV_CODEC_ID_BINKAUDIO_DCT = 86065,\n    CODEC_ID_BINKAUDIO_DCT = 86065,\n    AV_CODEC_ID_AAC_LATM = 86066,\n    CODEC_ID_AAC_LATM = 86066,\n    AV_CODEC_ID_QDMC = 86067,\n    CODEC_ID_QDMC = 86067,\n    AV_CODEC_ID_CELT = 86068,\n    CODEC_ID_CELT = 86068,\n    AV_CODEC_ID_G723_1 = 86069,\n    CODEC_ID_G723_1 = 86069,\n    AV_CODEC_ID_G729 = 86070,\n    CODEC_ID_G729 = 86070,\n    AV_CODEC_ID_8SVX_EXP = 86071,\n    CODEC_ID_8SVX_EXP = 86071,\n    AV_CODEC_ID_8SVX_FIB = 86072,\n    CODEC_ID_8SVX_FIB = 86072,\n    AV_CODEC_ID_BMV_AUDIO = 86073,\n    CODEC_ID_BMV_AUDIO = 86073,\n    AV_CODEC_ID_RALF = 86074,\n    CODEC_ID_RALF = 86074,\n    AV_CODEC_ID_IAC = 86075,\n    CODEC_ID_IAC = 86075,\n    AV_CODEC_ID_ILBC = 86076,\n    CODEC_ID_ILBC = 86076,\n    AV_CODEC_ID_OPUS_DEPRECATED = 86077,\n    AV_CODEC_ID_COMFORT_NOISE = 86078,\n    AV_CODEC_ID_TAK_DEPRECATED = 86079,\n    AV_CODEC_ID_METASOUND = 86080,\n    AV_CODEC_ID_PAF_AUDIO_DEPRECATED = 86081,\n    AV_CODEC_ID_ON2AVC = 86082,\n    AV_CODEC_ID_DVD_SUBTITLE = 94208,\n    AV_CODEC_ID_FIRST_SUBTITLE = 94208,\n    CODEC_ID_DVD_SUBTITLE = 94208,\n    CODEC_ID_FIRST_SUBTITLE = 94208,\n    AV_CODEC_ID_DVB_SUBTITLE = 94209,\n    CODEC_ID_DVB_SUBTITLE = 94209,\n    AV_CODEC_ID_TEXT = 94210,\n    CODEC_ID_TEXT = 94210,\n    AV_CODEC_ID_XSUB = 94211,\n    CODEC_ID_XSUB = 94211,\n    AV_CODEC_ID_SSA = 94212,\n    CODEC_ID_SSA = 94212,\n    AV_CODEC_ID_MOV_TEXT = 94213,\n    CODEC_ID_MOV_TEXT = 94213,\n    AV_CODEC_ID_HDMV_PGS_SUBTITLE = 94214,\n    CODEC_ID_HDMV_PGS_SUBTITLE = 94214,\n    AV_CODEC_ID_DVB_TELETEXT = 94215,\n    CODEC_ID_DVB_TELETEXT = 94215,\n    AV_CODEC_ID_SRT = 94216,\n    CODEC_ID_SRT = 94216,\n    AV_CODEC_ID_FIRST_UNKNOWN = 98304,\n    AV_CODEC_ID_TTF = 98304,\n    CODEC_ID_FIRST_UNKNOWN = 98304,\n    CODEC_ID_TTF = 98304,\n    AV_CODEC_ID_PROBE = 102400,\n    CODEC_ID_PROBE = 102400,\n    AV_CODEC_ID_MPEG2TS = 131072,\n    CODEC_ID_MPEG2TS = 131072,\n    AV_CODEC_ID_MPEG4SYSTEMS = 131073,\n    CODEC_ID_MPEG4SYSTEMS = 131073,\n    AV_CODEC_ID_FFMETADATA = 135168,\n    CODEC_ID_FFMETADATA = 135168,\n    AV_CODEC_ID_G2M = 4665933,\n    CODEC_ID_G2M = 4665933,\n    AV_CODEC_ID_IDF = 4801606,\n    CODEC_ID_IDF = 4801606,\n    AV_CODEC_ID_OTF = 5198918,\n    CODEC_ID_OTF = 5198918,\n    AV_CODEC_ID_PCM_S24LE_PLANAR = 407917392,\n    AV_CODEC_ID_PCM_S32LE_PLANAR = 542135120,\n    AV_CODEC_ID_012V = 808530518,\n    AV_CODEC_ID_EXR = 809850962,\n    CODEC_ID_EXR = 809850962,\n    AV_CODEC_ID_ADPCM_G726LE = 909260615,\n    AV_CODEC_ID_ADPCM_AFC = 1095123744,\n    AV_CODEC_ID_APNG = 1095781959,\n    AV_CODEC_ID_ASS = 1095979808,\n    AV_CODEC_ID_AVRP = 1096176208,\n    CODEC_ID_AVRP = 1096176208,\n    AV_CODEC_ID_AVRN = 1096176238,\n    AV_CODEC_ID_AVUI = 1096176969,\n    CODEC_ID_AVUI = 1096176969,\n    AV_CODEC_ID_AYUV = 1096373590,\n    CODEC_ID_AYUV = 1096373590,\n    AV_CODEC_ID_BRENDER_PIX = 1112557912,\n    AV_CODEC_ID_BINTEXT = 1112823892,\n    CODEC_ID_BINTEXT = 1112823892,\n    AV_CODEC_ID_CPIA = 1129335105,\n    AV_CODEC_ID_BIN_DATA = 1145132097,\n    AV_CODEC_ID_DVD_NAV = 1145979222,\n    AV_CODEC_ID_DSD_LSBF_PLANAR = 1146307633,\n    AV_CODEC_ID_DSD_MSBF_PLANAR = 1146307640,\n    AV_CODEC_ID_DSD_LSBF = 1146307660,\n    AV_CODEC_ID_DSD_MSBF = 1146307661,\n    AV_CODEC_ID_ADPCM_DTK = 1146374944,\n    AV_CODEC_ID_ESCAPE130 = 1160852272,\n    CODEC_ID_ESCAPE130 = 1160852272,\n    AV_CODEC_ID_FFWAVESYNTH = 1179014995,\n    CODEC_ID_FFWAVESYNTH = 1179014995,\n    AV_CODEC_ID_HEVC = 1211250229,\n    AV_CODEC_ID_JACOSUB = 1246975298,\n    CODEC_ID_JACOSUB = 1246975298,\n    AV_CODEC_ID_SMPTE_KLV = 1263294017,\n    AV_CODEC_ID_MPL2 = 1297108018,\n    AV_CODEC_ID_MVC1 = 1297498929,\n    AV_CODEC_ID_MVC2 = 1297498930,\n    AV_CODEC_ID_ADPCM_IMA_OKI = 1330333984,\n    AV_CODEC_ID_OPUS = 1330664787,\n    CODEC_ID_OPUS = 1330664787,\n    AV_CODEC_ID_PAF_AUDIO = 1346455105,\n    CODEC_ID_PAF_AUDIO = 1346455105,\n    AV_CODEC_ID_PAF_VIDEO = 1346455126,\n    CODEC_ID_PAF_VIDEO = 1346455126,\n    AV_CODEC_ID_PCM_S16BE_PLANAR = 1347637264,\n    AV_CODEC_ID_PJS = 1349012051,\n    AV_CODEC_ID_ADPCM_IMA_RAD = 1380008992,\n    AV_CODEC_ID_REALTEXT = 1381259348,\n    CODEC_ID_REALTEXT = 1381259348,\n    AV_CODEC_ID_SAMI = 1396788553,\n    CODEC_ID_SAMI = 1396788553,\n    AV_CODEC_ID_SANM = 1396788813,\n    CODEC_ID_SANM = 1396788813,\n    AV_CODEC_ID_SGIRLE = 1397180754,\n    AV_CODEC_ID_SMVJPEG = 1397577290,\n    AV_CODEC_ID_SNOW = 1397641047,\n    CODEC_ID_SNOW = 1397641047,\n    AV_CODEC_ID_SONIC = 1397706307,\n    CODEC_ID_SONIC = 1397706307,\n    AV_CODEC_ID_SONIC_LS = 1397706316,\n    CODEC_ID_SONIC_LS = 1397706316,\n    AV_CODEC_ID_SUBRIP = 1397909872,\n    AV_CODEC_ID_SUBVIEWER1 = 1398953521,\n    AV_CODEC_ID_STL = 1399870540,\n    AV_CODEC_ID_SUBVIEWER = 1400201814,\n    CODEC_ID_SUBVIEWER = 1400201814,\n    AV_CODEC_ID_TARGA_Y216 = 1412575542,\n    AV_CODEC_ID_TIMED_ID3 = 1414087731,\n    AV_CODEC_ID_V308 = 1446195256,\n    CODEC_ID_V308 = 1446195256,\n    AV_CODEC_ID_V408 = 1446260792,\n    CODEC_ID_V408 = 1446260792,\n    AV_CODEC_ID_ADPCM_VIMA = 1447644481,\n    AV_CODEC_ID_VIMA = 1447644481,\n    CODEC_ID_VIMA = 1447644481,\n    AV_CODEC_ID_VP7 = 1448097584,\n    AV_CODEC_ID_VPLAYER = 1448111218,\n    AV_CODEC_ID_WEBP = 1464156752,\n    AV_CODEC_ID_WEBVTT = 1465275476,\n    AV_CODEC_ID_XBIN = 1480739150,\n    CODEC_ID_XBIN = 1480739150,\n    AV_CODEC_ID_XFACE = 1480999235,\n    AV_CODEC_ID_Y41P = 1496592720,\n    CODEC_ID_Y41P = 1496592720,\n    AV_CODEC_ID_YUV4 = 1498764852,\n    CODEC_ID_YUV4 = 1498764852,\n    AV_CODEC_ID_EIA_608 = 1664495672,\n    CODEC_ID_EIA_608 = 1664495672,\n    AV_CODEC_ID_MICRODVD = 1833195076,\n    CODEC_ID_MICRODVD = 1833195076,\n    AV_CODEC_ID_EVRC = 1936029283,\n    AV_CODEC_ID_SMV = 1936944502,\n    AV_CODEC_ID_TAK = 1950507339\n} AVCodecID;\n\ntypedef struct AVIOContext AVIOContext;\n\ntypedef struct AVStream AVStream;\n\ntypedef struct AVProgram AVProgram;\n\ntypedef struct AVChapter AVChapter;\n\ntypedef struct AVDictionary AVDictionary;\n\ntypedef struct AVIOInterruptCB AVIOInterruptCB;\n\ntypedef enum AVDurationEstimationMethod\n{\n    AVFMT_DURATION_FROM_PTS = 0,\n    AVFMT_DURATION_FROM_STREAM = 1,\n    AVFMT_DURATION_FROM_BITRATE = 2\n} AVDurationEstimationMethod;\n\ntypedef struct AVPacketList AVPacketList;\n\ntypedef struct AVRational AVRational;\n\ntypedef struct AVFormatInternal AVFormatInternal;\n\ntypedef struct AVCodec AVCodec;\n\ntypedef struct AVSubtitle AVSubtitle;\n\ntypedef enum AUDIO_PB_STATE\n{\n    AUDIO_PB_IDLE = 0,\n    AUDIO_PB_PREPARING = 1,\n    AUDIO_PB_PLAYING = 2,\n    AUDIO_PB_PAUSED = 3,\n    AUDIO_PB_EOF = 4,\n    AUDIO_PB_ERROR = 5,\n    AUDIO_PB_CLOSED = 6\n} AUDIO_PB_STATE;\n\ntypedef enum AUDIO_PB_STATE audio_pb_state_t;\n\ntypedef struct pl_decoder_t pl_decoder_t;\n\ntypedef struct pl_decoder_t *pl_decoder_handle_t;\n\ntypedef struct pl_decoder_itf_t pl_decoder_itf_t;\n\ntypedef int (*input_data_cb)(void *, void *, int, int);\n\ntypedef int (*output_data_cb)(void *, void *, int, int);\n\ntypedef struct audio_resampler_t audio_resampler_t;\n\ntypedef struct audio_resampler_t *audio_resampler_ptr;\n\ntypedef enum VDEC_STATE\n{\n    VDEC_IDLE = 0,\n    VDEC_PREPARING = 1,\n    VDEC_PLAYING = 2,\n    VDEC_PAUSED = 3,\n    VDEC_EOF = 4,\n    VDEC_ERROR = 5,\n    VDEC_CLOSED = 6\n} VDEC_STATE;\n\ntypedef enum VDEC_STATE vdec_state_t;\n\ntypedef enum DUSS_FILE_FORMAT\n{\n    DUSS_FILE_FORMAT_NULL = 0,\n    DUSS_FILE_FORMAT_H264RAW = 1,\n    DUSS_FILE_FORMAT_H265RAW = 2,\n    DUSS_FILE_FORMAT_AACRAW = 3,\n    DUSS_FILE_FORMAT_AACADTSRAW = 4,\n    DUSS_FILE_FORMAT_MJPGRAW = 5,\n    DUSS_FILE_FORMAT_PRORESRAW = 6,\n    DUSS_FILE_FORMAT_PRORESRAWRAW = 7,\n    DUSS_FILE_FORMAT_USERDATARAW = 8,\n    DUSS_FILE_FORMAT_MPEG4RAW = 9,\n    DUSS_FILE_FORMAT_RAWMAX = 10,\n    DUSS_FILE_FORMAT_MP4 = 11,\n    DUSS_FILE_FORMAT_MOV = 12,\n    DUSS_FILE_FORMAT_MP2TS = 13,\n    DUSS_FILE_FORMAT_MAX = 14,\n    DUSS_FILE_FORMAT_NOT_SUPPORTED_YET = 14\n} DUSS_FILE_FORMAT;\n\ntypedef int (*gs_consumer_prepare_t)(void *);\n\ntypedef struct bridge_io_pkt bridge_io_pkt;\n\ntypedef struct bridge_io_pkt bridge_io_pkt_t;\n\ntypedef int (*gs_consumer_finish_t)(void *);\n\ntypedef struct duss_hal_mem_config_t duss_hal_mem_config_t;\n\ntypedef struct duss_hal_mem_param_t duss_hal_mem_param_t;\n\ntypedef struct duss_hal_mem_buf duss_hal_mem_buf;\n\ntypedef struct duss_hal_mem_buf *duss_hal_mem_handle_t;\n\ntypedef enum AVMediaType\n{\n    AVMEDIA_TYPE_UNKNOWN = -1,\n    AVMEDIA_TYPE_VIDEO = 0,\n    AVMEDIA_TYPE_AUDIO = 1,\n    AVMEDIA_TYPE_DATA = 2,\n    AVMEDIA_TYPE_SUBTITLE = 3,\n    AVMEDIA_TYPE_ATTACHMENT = 4,\n    AVMEDIA_TYPE_NB = 5\n} AVMediaType;\n\ntypedef struct AVCodecInternal AVCodecInternal;\n\ntypedef enum AVPictureType\n{\n    AV_PICTURE_TYPE_NONE = 0,\n    AV_PICTURE_TYPE_I = 1,\n    AV_PICTURE_TYPE_P = 2,\n    AV_PICTURE_TYPE_B = 3,\n    AV_PICTURE_TYPE_S = 4,\n    AV_PICTURE_TYPE_SI = 5,\n    AV_PICTURE_TYPE_SP = 6,\n    AV_PICTURE_TYPE_BI = 7\n} AVPictureType;\n\ntypedef struct AVPanScan AVPanScan;\n\ntypedef struct AVBufferRef AVBufferRef;\n\ntypedef struct AVFrameSideData AVFrameSideData;\n\ntypedef enum AVColorRange\n{\n    AVCOL_RANGE_UNSPECIFIED = 0,\n    AVCOL_RANGE_MPEG = 1,\n    AVCOL_RANGE_JPEG = 2,\n    AVCOL_RANGE_NB = 3\n} AVColorRange;\n\ntypedef enum AVColorPrimaries\n{\n    AVCOL_PRI_RESERVED0 = 0,\n    AVCOL_PRI_BT709 = 1,\n    AVCOL_PRI_UNSPECIFIED = 2,\n    AVCOL_PRI_RESERVED = 3,\n    AVCOL_PRI_BT470M = 4,\n    AVCOL_PRI_BT470BG = 5,\n    AVCOL_PRI_SMPTE170M = 6,\n    AVCOL_PRI_SMPTE240M = 7,\n    AVCOL_PRI_FILM = 8,\n    AVCOL_PRI_BT2020 = 9,\n    AVCOL_PRI_NB = 10\n} AVColorPrimaries;\n\ntypedef enum AVColorTransferCharacteristic\n{\n    AVCOL_TRC_RESERVED0 = 0,\n    AVCOL_TRC_BT709 = 1,\n    AVCOL_TRC_UNSPECIFIED = 2,\n    AVCOL_TRC_RESERVED = 3,\n    AVCOL_TRC_GAMMA22 = 4,\n    AVCOL_TRC_GAMMA28 = 5,\n    AVCOL_TRC_SMPTE170M = 6,\n    AVCOL_TRC_SMPTE240M = 7,\n    AVCOL_TRC_LINEAR = 8,\n    AVCOL_TRC_LOG = 9,\n    AVCOL_TRC_LOG_SQRT = 10,\n    AVCOL_TRC_IEC61966_2_4 = 11,\n    AVCOL_TRC_BT1361_ECG = 12,\n    AVCOL_TRC_IEC61966_2_1 = 13,\n    AVCOL_TRC_BT2020_10 = 14,\n    AVCOL_TRC_BT2020_12 = 15,\n    AVCOL_TRC_NB = 16\n} AVColorTransferCharacteristic;\n\ntypedef enum AVColorSpace\n{\n    AVCOL_SPC_RGB = 0,\n    AVCOL_SPC_BT709 = 1,\n    AVCOL_SPC_UNSPECIFIED = 2,\n    AVCOL_SPC_RESERVED = 3,\n    AVCOL_SPC_FCC = 4,\n    AVCOL_SPC_BT470BG = 5,\n    AVCOL_SPC_SMPTE170M = 6,\n    AVCOL_SPC_SMPTE240M = 7,\n    AVCOL_SPC_YCOCG = 8,\n    AVCOL_SPC_BT2020_NCL = 9,\n    AVCOL_SPC_BT2020_CL = 10,\n    AVCOL_SPC_NB = 11\n} AVColorSpace;\n\ntypedef enum AVChromaLocation\n{\n    AVCHROMA_LOC_UNSPECIFIED = 0,\n    AVCHROMA_LOC_LEFT = 1,\n    AVCHROMA_LOC_CENTER = 2,\n    AVCHROMA_LOC_TOPLEFT = 3,\n    AVCHROMA_LOC_TOP = 4,\n    AVCHROMA_LOC_BOTTOMLEFT = 5,\n    AVCHROMA_LOC_BOTTOM = 6,\n    AVCHROMA_LOC_NB = 7\n} AVChromaLocation;\n\ntypedef enum AVFieldOrder\n{\n    AV_FIELD_UNKNOWN = 0,\n    AV_FIELD_PROGRESSIVE = 1,\n    AV_FIELD_TT = 2,\n    AV_FIELD_BB = 3,\n    AV_FIELD_TB = 4,\n    AV_FIELD_BT = 5\n} AVFieldOrder;\n\ntypedef enum AVSampleFormat\n{\n    AV_SAMPLE_FMT_NONE = -1,\n    AV_SAMPLE_FMT_U8 = 0,\n    AV_SAMPLE_FMT_S16 = 1,\n    AV_SAMPLE_FMT_S32 = 2,\n    AV_SAMPLE_FMT_FLT = 3,\n    AV_SAMPLE_FMT_DBL = 4,\n    AV_SAMPLE_FMT_U8P = 5,\n    AV_SAMPLE_FMT_S16P = 6,\n    AV_SAMPLE_FMT_S32P = 7,\n    AV_SAMPLE_FMT_FLTP = 8,\n    AV_SAMPLE_FMT_DBLP = 9,\n    AV_SAMPLE_FMT_NB = 10\n} AVSampleFormat;\n\ntypedef enum AVAudioServiceType\n{\n    AV_AUDIO_SERVICE_TYPE_MAIN = 0,\n    AV_AUDIO_SERVICE_TYPE_EFFECTS = 1,\n    AV_AUDIO_SERVICE_TYPE_VISUALLY_IMPAIRED = 2,\n    AV_AUDIO_SERVICE_TYPE_HEARING_IMPAIRED = 3,\n    AV_AUDIO_SERVICE_TYPE_DIALOGUE = 4,\n    AV_AUDIO_SERVICE_TYPE_COMMENTARY = 5,\n    AV_AUDIO_SERVICE_TYPE_EMERGENCY = 6,\n    AV_AUDIO_SERVICE_TYPE_VOICE_OVER = 7,\n    AV_AUDIO_SERVICE_TYPE_KARAOKE = 8,\n    AV_AUDIO_SERVICE_TYPE_NB = 9\n} AVAudioServiceType;\n\ntypedef struct RcOverride RcOverride;\n\ntypedef struct AVHWAccel AVHWAccel;\n\ntypedef struct MpegEncContext MpegEncContext;\n\ntypedef enum AVDiscard\n{\n    AVDISCARD_NONE = -16,\n    AVDISCARD_DEFAULT = 0,\n    AVDISCARD_NONREF = 8,\n    AVDISCARD_BIDIR = 16,\n    AVDISCARD_NONINTRA = 24,\n    AVDISCARD_NONKEY = 32,\n    AVDISCARD_ALL = 48\n} AVDiscard;\n\ntypedef struct AVCodecDescriptor AVCodecDescriptor;\n\ntypedef struct pl_muxer_itf_t pl_muxer_itf_t;\n\ntypedef enum pl_muxer_config_index_t\n{\n    PL_MUXER_CONFIG_INDEX_NONE = 0,\n    PL_MUXER_CONFIG_INDEX_EXTRADATA = 1,\n    PL_MUXER_CONFIG_INDEX_FRAGMENTED_MP4 = 2\n} pl_muxer_config_index_t;\n\ntypedef struct __sbuf __sbuf;\n\ntypedef enum pcm_format\n{\n    PCM_FORMAT_INVALID = -1,\n    PCM_FORMAT_S16_LE = 0,\n    PCM_FORMAT_S32_LE = 1,\n    PCM_FORMAT_S8 = 2,\n    PCM_FORMAT_S24_LE = 3,\n    PCM_FORMAT_S24_3LE = 4,\n    PCM_FORMAT_MAX = 5\n} pcm_format;\n\ntypedef enum pcm_tstamp\n{\n    NONE_TSTAMP = 0,\n    ALSA_TSTAMP = 1,\n    ALSA_TSTAMP_SYNC = 2,\n    TSTAMP_TYPE_MAX = 3\n} pcm_tstamp;\n\ntypedef struct snd_pcm_mmap_status snd_pcm_mmap_status;\n\ntypedef struct snd_pcm_mmap_control snd_pcm_mmap_control;\n\ntypedef struct snd_pcm_sync_ptr snd_pcm_sync_ptr;\n\ntypedef union anon_union_conflict2c9b_for_field_3 anon_union_conflict2c9b_for_field_3;\n\ntypedef struct stick_mode_self_define_t stick_mode_self_define_t;\n\ntypedef struct loc_channel_addr loc_channel_addr;\n\ntypedef struct ip_channel_addr ip_channel_addr;\n\ntypedef struct wl_channel_addr wl_channel_addr;\n\ntypedef struct uart_channel_addr uart_channel_addr;\n\ntypedef struct can_channel_addr can_channel_addr;\n\ntypedef struct i2c_channel_addr i2c_channel_addr;\n\ntypedef struct spi_channel_addr spi_channel_addr;\n\ntypedef struct hpi_channel_addr hpi_channel_addr;\n\ntypedef struct usb_channel_addr usb_channel_addr;\n\ntypedef struct usbacc_channel_addr usbacc_channel_addr;\n\ntypedef struct icc_channel_addr icc_channel_addr;\n\ntypedef struct netlink_channel_addr netlink_channel_addr;\n\ntypedef struct bulk_channel_addr bulk_channel_addr;\n\ntypedef enum duss_storage_client_type_t\n{\n    DUSS_STORAGE_EMMC0 = 0,\n    DUSS_STORAGE_SDCARD0 = 1,\n    DUSS_STORAGE_SDCARD1 = 2,\n    DUSS_STORAGE_UDISK = 3\n} duss_storage_client_type_t;\n\ntypedef struct anon_struct_conflict2349 anon_struct_conflict2349;\n\ntypedef struct anon_struct_conflict242a anon_struct_conflict242a;\n\ntypedef struct gs_local_video_info gs_local_video_info;\n\ntypedef struct gs_local_video_info gs_local_video_info_t;\n\ntypedef struct gs_local_panorama_info gs_local_panorama_info;\n\ntypedef struct gs_local_panorama_info gs_local_panorama_info_t;\n\ntypedef struct AVOption AVOption;\n\ntypedef struct AVOptionRange AVOptionRange;\n\ntypedef struct AVCodecTag AVCodecTag;\n\ntypedef struct AVPacketSideData AVPacketSideData;\n\ntypedef struct AVFrac AVFrac;\n\ntypedef struct anon_struct_conflict69b3c_for_info anon_struct_conflict69b3c_for_info;\n\ntypedef enum AVStreamParseType\n{\n    AVSTREAM_PARSE_NONE = 0,\n    AVSTREAM_PARSE_FULL = 1,\n    AVSTREAM_PARSE_HEADERS = 2,\n    AVSTREAM_PARSE_TIMESTAMPS = 3,\n    AVSTREAM_PARSE_FULL_ONCE = 4,\n    AVSTREAM_PARSE_FULL_RAW = 1463898624\n} AVStreamParseType;\n\ntypedef struct AVCodecParserContext AVCodecParserContext;\n\ntypedef struct AVIndexEntry AVIndexEntry;\n\ntypedef struct AVProfile AVProfile;\n\ntypedef struct AVCodecDefault AVCodecDefault;\n\ntypedef struct AVSubtitleRect AVSubtitleRect;\n\ntypedef struct SwrContext SwrContext;\n\ntypedef struct AVBuffer AVBuffer;\n\ntypedef enum AVFrameSideDataType\n{\n    AV_FRAME_DATA_PANSCAN = 0,\n    AV_FRAME_DATA_A53_CC = 1,\n    AV_FRAME_DATA_STEREO3D = 2,\n    AV_FRAME_DATA_MATRIXENCODING = 3,\n    AV_FRAME_DATA_DOWNMIX_INFO = 4,\n    AV_FRAME_DATA_REPLAYGAIN = 5,\n    AV_FRAME_DATA_DISPLAYMATRIX = 6,\n    AV_FRAME_DATA_AFD = 7,\n    AV_FRAME_DATA_MOTION_VECTORS = 8,\n    AV_FRAME_DATA_SKIP_SAMPLES = 9\n} AVFrameSideDataType;\n\ntypedef struct _DUSS_MSG_APERTURE_t _DUSS_MSG_APERTURE_t;\n\ntypedef struct _DUSS_MSG_APERTURE_t DUSS_MSG_APERTURE_t;\n\ntypedef struct _DUSS_MSG_SHUTTER_SPEED_t _DUSS_MSG_SHUTTER_SPEED_t;\n\ntypedef struct _DUSS_MSG_SHUTTER_SPEED_t DUSS_MSG_SHUTTER_SPEED_t;\n\ntypedef struct _DUSS_MSG_ISO_t _DUSS_MSG_ISO_t;\n\ntypedef struct _DUSS_MSG_ISO_t DUSS_MSG_ISO_t;\n\ntypedef struct _DUSS_MSG_EXPO_MODE_t _DUSS_MSG_EXPO_MODE_t;\n\ntypedef struct _DUSS_MSG_EXPO_MODE_t DUSS_MSG_EXPO_MODE_t;\n\ntypedef struct _DUSS_MSG_EV_BIAS_t _DUSS_MSG_EV_BIAS_t;\n\ntypedef struct _DUSS_MSG_EV_BIAS_t DUSS_MSG_EV_BIAS_t;\n\ntypedef struct _DUSS_MSG_P_STORAGE_FMT_t _DUSS_MSG_P_STORAGE_FMT_t;\n\ntypedef struct _DUSS_MSG_P_STORAGE_FMT_t DUSS_MSG_P_STORAGE_FMT_t;\n\ntypedef struct _DUSS_MSG_WB_t _DUSS_MSG_WB_t;\n\ntypedef struct _DUSS_MSG_WB_t DUSS_MSG_WB_t;\n\ntypedef struct _DUSS_MSG_SCENE_MODE_t _DUSS_MSG_SCENE_MODE_t;\n\ntypedef struct _DUSS_MSG_SCENE_MODE_t DUSS_MSG_SCENE_MODE_t;\n\ntypedef struct _DUSS_MSG_DIGITAL_EFFECT_t _DUSS_MSG_DIGITAL_EFFECT_t;\n\ntypedef struct _DUSS_MSG_DIGITAL_EFFECT_t DUSS_MSG_DIGITAL_EFFECT_t;\n\ntypedef struct _DUSS_MSG_P_SIZE_t _DUSS_MSG_P_SIZE_t;\n\ntypedef struct _DUSS_MSG_P_SIZE_t DUSS_MSG_P_SIZE_t;\n\ntypedef struct _DUSS_MSG_SET_FOCUS_REGION_t _DUSS_MSG_SET_FOCUS_REGION_t;\n\ntypedef struct _DUSS_MSG_SET_FOCUS_REGION_t DUSS_MSG_SET_FOCUS_REGION_t;\n\ntypedef struct _DUSS_MSG_CAPTURE_MODE_t _DUSS_MSG_CAPTURE_MODE_t;\n\ntypedef struct _DUSS_MSG_CAPTURE_MODE_t DUSS_MSG_CAPTURE_MODE_t;\n\ntypedef struct _DUSS_MSG_CONTICAP_PARAM_t _DUSS_MSG_CONTICAP_PARAM_t;\n\ntypedef struct _DUSS_MSG_CONTICAP_PARAM_t DUSS_MSG_CONTICAP_PARAM_t;\n\ntypedef struct _DUSS_MSG_V_STORAGE_FMT_t _DUSS_MSG_V_STORAGE_FMT_t;\n\ntypedef struct _DUSS_MSG_V_STORAGE_FMT_t DUSS_MSG_V_STORAGE_FMT_t;\n\ntypedef struct _DUSS_MSG_V_FORMAT_t _DUSS_MSG_V_FORMAT_t;\n\ntypedef struct _DUSS_MSG_V_FORMAT_t DUSS_MSG_V_FORMAT_t;\n\ntypedef struct _DUSS_MSG_VIDEO_STANDARD_t _DUSS_MSG_VIDEO_STANDARD_t;\n\ntypedef struct _DUSS_MSG_VIDEO_STANDARD_t DUSS_MSG_VIDEO_STANDARD_t;\n\ntypedef struct _DUSS_MSG_WORKMODE_t _DUSS_MSG_WORKMODE_t;\n\ntypedef struct _DUSS_MSG_WORKMODE_t DUSS_MSG_WORKMODE_t;\n\ntypedef struct _DUSS_MSG_SINGLE_PLAY_CRTL_t _DUSS_MSG_SINGLE_PLAY_CRTL_t;\n\ntypedef struct _DUSS_MSG_SINGLE_PLAY_CRTL_t DUSS_MSG_SINGLE_PLAY_CRTL_t;\n\ntypedef struct _DUSS_MSG_V_CRTL_t _DUSS_MSG_V_CRTL_t;\n\ntypedef struct _DUSS_MSG_V_CRTL_t DUSS_MSG_V_CRTL_t;\n\ntypedef struct _DUSS_MSG_SD_INFO_t _DUSS_MSG_SD_INFO_t;\n\ntypedef struct _DUSS_MSG_SD_INFO_t DUSS_MSG_SD_INFO_t;\n\ntypedef struct _DUSS_MSG_ZOOM_PARAM_t _DUSS_MSG_ZOOM_PARAM_t;\n\ntypedef struct _DUSS_MSG_ZOOM_PARAM_t DUSS_MSG_ZOOM_PARAM_t;\n\ntypedef struct _DUSS_MSG_FLIP _DUSS_MSG_FLIP;\n\ntypedef struct _DUSS_MSG_FLIP DUSS_MSG_FLIP_t;\n\ntypedef struct _DUSS_MSG_SHARPNESS_t _DUSS_MSG_SHARPNESS_t;\n\ntypedef struct _DUSS_MSG_SHARPNESS_t DUSS_MSG_SHARPNESS_t;\n\ntypedef struct _DUSS_MSG_CONTRAST_t _DUSS_MSG_CONTRAST_t;\n\ntypedef struct _DUSS_MSG_CONTRAST_t DUSS_MSG_CONTRAST_t;\n\ntypedef struct _DUSS_MSG_SATURATION_t _DUSS_MSG_SATURATION_t;\n\ntypedef struct _DUSS_MSG_SATURATION_t DUSS_MSG_SATURATION_t;\n\ntypedef struct _DUSS_MSG_CAPTURE_t _DUSS_MSG_CAPTURE_t;\n\ntypedef struct _DUSS_MSG_CAPTURE_t DUSS_MSG_CAPTURE_t;\n\ntypedef struct _DUSS_MSG_RECORD_t _DUSS_MSG_RECORD_t;\n\ntypedef struct _DUSS_MSG_RECORD_t DUSS_MSG_RECORD_t;\n\ntypedef struct _UAV_RECORD_MODE_t _UAV_RECORD_MODE_t;\n\ntypedef struct _UAV_RECORD_MODE_t UAV_RECORD_MODE_t;\n\ntypedef uint8_t duss_wl_prot_type_t;\n\ntypedef uint8_t duss_wl_prio_t;\n\ntypedef struct duss_hal_uart_config_t duss_hal_uart_config_t;\n\ntypedef struct duss_hal_can_config_t duss_hal_can_config_t;\n\ntypedef struct duss_hal_i2c_config_t duss_hal_i2c_config_t;\n\ntypedef struct duss_hal_spi_config_t duss_hal_spi_config_t;\n\ntypedef struct hpi_channel hpi_channel;\n\ntypedef struct hpi_channel duss_hal_hpi_config_t;\n\ntypedef struct duss_hal_usbacc_config_t duss_hal_usbacc_config_t;\n\ntypedef struct duss_hal_icc_config_t duss_hal_icc_config_t;\n\ntypedef struct duss_hal_bulk_config_t duss_hal_bulk_config_t;\n\ntypedef enum AVOptionType\n{\n    AV_OPT_TYPE_FLAGS = 0,\n    FF_OPT_TYPE_FLAGS = 0,\n    AV_OPT_TYPE_INT = 1,\n    FF_OPT_TYPE_INT = 1,\n    AV_OPT_TYPE_INT64 = 2,\n    FF_OPT_TYPE_INT64 = 2,\n    AV_OPT_TYPE_DOUBLE = 3,\n    FF_OPT_TYPE_DOUBLE = 3,\n    AV_OPT_TYPE_FLOAT = 4,\n    FF_OPT_TYPE_FLOAT = 4,\n    AV_OPT_TYPE_STRING = 5,\n    FF_OPT_TYPE_STRING = 5,\n    AV_OPT_TYPE_RATIONAL = 6,\n    FF_OPT_TYPE_RATIONAL = 6,\n    AV_OPT_TYPE_BINARY = 7,\n    FF_OPT_TYPE_BINARY = 7,\n    AV_OPT_TYPE_DICT = 8,\n    AV_OPT_TYPE_CONST = 128,\n    FF_OPT_TYPE_CONST = 128,\n    AV_OPT_TYPE_CHANNEL_LAYOUT = 1128811585,\n    AV_OPT_TYPE_COLOR = 1129270354,\n    AV_OPT_TYPE_DURATION = 1146442272,\n    AV_OPT_TYPE_PIXEL_FMT = 1346784596,\n    AV_OPT_TYPE_SAMPLE_FMT = 1397116244,\n    AV_OPT_TYPE_IMAGE_SIZE = 1397316165,\n    AV_OPT_TYPE_VIDEO_RATE = 1448231252\n} AVOptionType;\n\ntypedef union anon_union_conflictac8f4_for_default_val anon_union_conflictac8f4_for_default_val;\n\ntypedef enum AVPacketSideDataType\n{\n    AV_PKT_DATA_PALETTE = 0,\n    AV_PKT_DATA_NEW_EXTRADATA = 1,\n    AV_PKT_DATA_PARAM_CHANGE = 2,\n    AV_PKT_DATA_H263_MB_INFO = 3,\n    AV_PKT_DATA_REPLAYGAIN = 4,\n    AV_PKT_DATA_DISPLAYMATRIX = 5,\n    AV_PKT_DATA_STEREO3D = 6,\n    AV_PKT_DATA_SKIP_SAMPLES = 70,\n    AV_PKT_DATA_JP_DUALMONO = 71,\n    AV_PKT_DATA_STRINGS_METADATA = 72,\n    AV_PKT_DATA_SUBTITLE_POSITION = 73,\n    AV_PKT_DATA_MATROSKA_BLOCKADDITIONAL = 74,\n    AV_PKT_DATA_WEBVTT_IDENTIFIER = 75,\n    AV_PKT_DATA_WEBVTT_SETTINGS = 76,\n    AV_PKT_DATA_METADATA_UPDATE = 77\n} AVPacketSideDataType;\n\ntypedef struct AVCodecParser AVCodecParser;\n\ntypedef enum AVPictureStructure\n{\n    AV_PICTURE_STRUCTURE_UNKNOWN = 0,\n    AV_PICTURE_STRUCTURE_TOP_FIELD = 1,\n    AV_PICTURE_STRUCTURE_BOTTOM_FIELD = 2,\n    AV_PICTURE_STRUCTURE_FRAME = 3\n} AVPictureStructure;\n\ntypedef struct AVPicture AVPicture;\n\ntypedef enum AVSubtitleType\n{\n    SUBTITLE_NONE = 0,\n    SUBTITLE_BITMAP = 1,\n    SUBTITLE_TEXT = 2,\n    SUBTITLE_ASS = 3\n} AVSubtitleType;\n\ntypedef struct _DUSS_MSG_SHUTTER_A_t _DUSS_MSG_SHUTTER_A_t;\n\ntypedef struct _DUSS_MSG_SHUTTER_A_t DUSS_MSG_SHUTTER_A_t;\n\ntypedef struct _DUSS_MSG_V_SS_CONFIG_t _DUSS_MSG_V_SS_CONFIG_t;\n\ntypedef struct _DUSS_MSG_V_SS_CONFIG_t DUSS_MSG_V_SS_CONFIG_t;\n\ntypedef struct _DUSS_MSG_SD_STATUS_t _DUSS_MSG_SD_STATUS_t;\n\ntypedef struct _DUSS_MSG_SD_STATUS_t DUSS_MSG_SD_STATUS_t;\n\ntypedef struct _DUSS_MSG_ZOOM_CTRL_MODE_t_ _DUSS_MSG_ZOOM_CTRL_MODE_t_;\n\ntypedef struct _DUSS_MSG_ZOOM_CTRL_MODE_t_ DUSS_MSG_ZOOM_CTRL_MODE_t;\n\ntypedef union anon_union_conflict21a2_for_OZoomRaUnion anon_union_conflict21a2_for_OZoomRaUnion;\n\ntypedef uint8_t duss_uart_parity_t;\n\ntypedef uint8_t duss_uart_stopbit_t;\n\ntypedef uint8_t duss_uart_wordlen_t;\n\ntypedef uint8_t duss_mb_can_chip_t;\n\ntypedef uint16_t duss_mb_host_id_t;\n\ntypedef uint8_t duss_i2c_addr_len_t;\n\ntypedef uint __u32;\n\ntypedef uchar __u8;\n\ntypedef ushort __u16;\n\ntypedef uint16_t duss_usbacc_port_t;\n\ntypedef enum duss_hal_icc_role_t\n{\n    DUSS_HAL_ICC_ROLE_SOURCE = 0,\n    DUSS_HAL_ICC_ROLE_TARGET = 1,\n    DUSS_HAL_ICC_ROLE_MAX = 2\n} duss_hal_icc_role_t;\n\ntypedef enum duss_hal_icc_shm_type_t\n{\n    DUSS_HAL_ICC_SHM_SRAM = 0,\n    DUSS_HAL_ICC_SHM_DDR = 1,\n    DUSS_HAL_ICC_SHM_MAX = 2\n} duss_hal_icc_shm_type_t;\n\ntypedef struct _DUSS_MSG_ZOOM_S_t _DUSS_MSG_ZOOM_S_t;\n\ntypedef struct _DUSS_MSG_ZOOM_S_t DUSS_MSG_ZOOM_S_t;\n\ntypedef struct _DUSS_MSG_ZOOM_P_t _DUSS_MSG_ZOOM_P_t;\n\ntypedef struct _DUSS_MSG_ZOOM_P_t DUSS_MSG_ZOOM_P_t;\n\ntypedef struct _DUSS_MSG_ZOOM_C_t _DUSS_MSG_ZOOM_C_t;\n\ntypedef struct _DUSS_MSG_ZOOM_C_t DUSS_MSG_ZOOM_C_t;\n\nstruct _DUSS_MSG_RACING_CHANNEL_OCCUPIED_IPSD_t\n{\n    uint16_t freq_idx;\n    uint8_t occupied_ind;\n    uint8_t work_ind;\n    uint32_t chn_ipsd;\n};\n\nstruct __gs_avin_test_ctrl\n{\n    struct duss_osal_task_handle_t *avin_test_task;\n    _Bool avin_test_task_quit;\n    _Bool avin_test_flag;\n    _Bool init;\n    undefined field4_0x7;\n};\n\nstruct gs_lv_csm\n{\n    char *name;\n    _Bool high_prior;\n    undefined field2_0x5;\n    undefined field3_0x6;\n    undefined field4_0x7;\n    void *ctx;\n    gs_consumer_prepare_t prepare;\n    int (*consume)(void *, gs_lv_pkt_t *);\n    gs_consumer_finish_t finish;\n};\n\nstruct gs_usb_gadget_vt\n{\n    int fdin;\n    int fdout;\n    gs_usb_listener_t *usb_listener;\n    struct duss_osal_task_handle_t *usb_out_task;\n    _Bool usb_task_quit;\n    _Bool is_wl_chnl_orig;\n    _Bool is_wl_chnl_cur;\n    undefined field7_0x13;\n    gs_lv_src_t *lv_src;\n    gs_lv_csm_t lv_csm;\n    gs_media_cmd_chnl_t *mcc;\n    gs_modem_ctrl_t *modem_ctrl;\n    int lv_igr_cnt;\n    uint32_t listener_id;\n};\n\nstruct gs_battery_voltage\n{\n    int32_t voltage;\n    float batt;\n    uint8_t series;\n    undefined field3_0x9;\n    undefined field4_0xa;\n    undefined field5_0xb;\n    enum batteryState_e state;\n};\n\nstruct gs_battery_info\n{\n    uint32_t interval;\n    char *addr;\n    gs_battery_voltage_t vol;\n};\n\nstruct gs_meta_listener\n{\n    void *ctx;\n    int (*event_cb)(void *, meta_event_t);\n};\n\nstruct metadata_retriever\n{\n    gs_meta_listener_t meta_listener;\n    struct duss_osal_mutex_handle_t *db_mutex;\n    struct sqlite3 *p_db;\n    _Bool b_parse_pending;\n    _Bool b_running;\n    undefined field5_0x12;\n    undefined field6_0x13;\n    struct duss_osal_task_handle_t *task_retriever;\n};\n\nstruct __gs_common_cmd_ctrl\n{\n    struct duss_osal_task_handle_t *cmn_cmd_task;\n    struct duss_osal_msgq_handle_t *cmn_cmd_queue;\n    _Bool cmn_cmd_task_quit;\n    undefined field3_0x9;\n    undefined field4_0xa;\n    undefined field5_0xb;\n    duss_event_client_handle_t event_client;\n    _Bool init;\n    undefined field8_0x11;\n    undefined field9_0x12;\n    undefined field10_0x13;\n    void *ctx;\n};\n\nstruct gs_media_cmd_chnl\n{\n    int chnl_fd;\n    _Bool init;\n    undefined field2_0x5;\n    undefined field3_0x6;\n    undefined field4_0x7;\n};\n\nstruct keys_pack_to_racing_glass_t\n{\n    uint8_t right_wheel_press : 2;\n    uint8_t right_wheel_scroll : 2;\n    uint8_t f1_press : 2;\n    uint8_t home : 2;\n    uint8_t left_wheel_scroll : 2;\n    uint8_t record_press : 2;\n    uint8_t reverved_2 : 2;\n    uint8_t reverved_3 : 2;\n};\n\nstruct gs_audio_wl\n{\n    void *private_data;\n};\n\nstruct anon_struct_conflictc3fb_for_sw\n{\n    uint8_t fun_key1 : 1;\n    uint8_t fun_key2 : 1;\n    uint8_t others : 6;\n};\n\nstruct anon_struct_conflictc431_for_key\n{\n    uint8_t up_5d : 1;\n    uint8_t down_5d : 1;\n    uint8_t left_5d : 1;\n    uint8_t right_5d : 1;\n    uint8_t ensure_5d : 1;\n    uint8_t cancel : 1;\n    uint8_t record : 1;\n    uint8_t pair : 1;\n};\n\nstruct factory_check\n{\n    uint32_t reserved : 31;\n    uint32_t result : 1;\n};\n\nstruct pack_for_factory_test_t\n{\n    struct factory_check check;\n    uint16_t not_use1[6];\n    struct anon_struct_conflictc3fb_for_sw sw;\n    struct anon_struct_conflictc431_for_key key;\n    uint8_t not_use2[26];\n};\n\nstruct gs_shram\n{\n    int fd;\n    uint8_t *addr;\n    int phy_size;\n    int map_offset;\n    int size;\n};\n\nstruct gs_watermarker_us\n{\n    uint8_t watermarker_flag;\n    undefined field1_0x1;\n    undefined field2_0x2;\n    undefined field3_0x3;\n    int dev_mem;\n    void *uncal_nvram_va;\n    void *uncal_nvram_align_va;\n    size_t uncal_nvram_map_sz;\n};\n\nstruct gs_watermarker_ctrl\n{\n    gs_media_cmd_chnl_t *mcc;\n    gs_watermarker_us_t watermarker_us;\n};\n\nstruct __gs_queue\n{\n    uchar *addr;\n    uint size;\n    uint num;\n    uint writer;\n    uint reader;\n};\n\nstruct __gs_gui\n{\n    _Bool gui_en;\n    undefined field1_0x1;\n    undefined field2_0x2;\n    undefined field3_0x3;\n    void *context;\n    void *dl_hdl;\n    gs_queue_t racing_drone_fc_osd_queue;\n    int (*initialize)(void **, gs_gui_config_t *);\n    void (*destroy)(void *);\n    int (*run)(void *);\n    int (*send_event)(void *, struct gs_gui_event_t *);\n};\n\nstruct __gs_local_sd_info\n{\n    duss_storage_client_t *sdcard_cli;\n    _Bool sd_insert;\n    undefined field2_0x5;\n    undefined field3_0x6;\n    undefined field4_0x7;\n    sd_file_type_t sd_type;\n    uint32_t sd_total_kbytes;\n    uint32_t sd_free_kbytes;\n    uint32_t sd_minfree_kbytes;\n    uint8_t sd_status;\n    uint8_t sd_formatting;\n    _Bool sd_init;\n    undefined field12_0x1b;\n    struct duss_osal_mutex_handle_t *sd_status_mutex;\n    gs_storage_listener_t *storage_listener;\n    gs_sd_listener_t *sd_listener;\n    gs_meta_listener_t *meta_listener;\n    gs_playback_listener_t *pb_listener;\n};\n\nstruct rc_set_endpoint_t\n{\n    uint8_t ep_min;\n    uint8_t ep_max;\n};\n\nstruct rc_set_all_ep_t\n{\n    struct rc_set_endpoint_t a_ch;\n    struct rc_set_endpoint_t e_ch;\n    struct rc_set_endpoint_t t_ch;\n    struct rc_set_endpoint_t r_ch;\n};\n\nstruct dummy_ui_ctx_t\n{\n    _Bool client;\n    undefined field1_0x1;\n    undefined field2_0x2;\n    undefined field3_0x3;\n    int sock_fd;\n    _Bool quit;\n    undefined field6_0x9;\n    undefined field7_0xa;\n    undefined field8_0xb;\n    struct duss_osal_task_handle_t *console_task;\n    struct duss_osal_timer_handle_t *console_timer;\n    struct duss_osal_event_handle_t *event;\n    struct duss_osal_task_handle_t *reader_task;\n    int (*msg_cb)(void *, void *);\n};\n\nstruct rc_set_subtrim_reverse_t\n{\n    uint16_t reserved : 6;\n    uint16_t reverse : 1;\n    uint16_t subtrim : 9;\n};\n\nstruct rc_set_all_st_and_rev_t\n{\n    struct rc_set_subtrim_reverse_t a_ch;\n    struct rc_set_subtrim_reverse_t e_ch;\n    struct rc_set_subtrim_reverse_t t_ch;\n    struct rc_set_subtrim_reverse_t r_ch;\n};\n\nstruct rc_set_subtrim_t\n{\n    int16_t st_value;\n};\n\nstruct rc_set_all_st_t\n{\n    struct rc_set_subtrim_t a_ch;\n    struct rc_set_subtrim_t e_ch;\n    struct rc_set_subtrim_t t_ch;\n    struct rc_set_subtrim_t r_ch;\n};\n\nstruct anon_struct_conflict47e0\n{\n    uint8_t reserved : 4;\n    uint8_t is_reverse_r : 1;\n    uint8_t is_reverse_t : 1;\n    uint8_t is_reverse_e : 1;\n    uint8_t is_reverse_a : 1;\n};\n\nunion rc_set_reverse_t\n{\n    struct anon_struct_conflict47e0 rev_bit;\n    uint8_t rev_byte;\n};\n\nstruct gs_rc_ctrl\n{\n    struct duss_osal_msgq_handle_t *rc_cmd_queue;\n    struct duss_osal_task_handle_t *rc_cmd_task;\n    _Bool rc_cmd_task_quit;\n    _Bool init;\n    union rc_set_reverse_t reverse_setting;\n    int8_t reverse_valid;\n    struct rc_set_all_st_t subtrim_setting;\n    int8_t subtrim_valid;\n    struct rc_set_all_ep_t endpoint_setting;\n    int8_t endpoint_valid;\n    uint8_t stick_mode;\n    int8_t stick_valid;\n    char rc_firm_ver[128];\n    char rc_sn[20];\n    int rc_mp_state;\n    int rc_active_state;\n    int8_t version_valid;\n    struct rc_set_all_st_and_rev_t subtrim_endpiont_setting;\n    uint8_t rc_lock_stat;\n    _Bool rc_monitor_quit;\n    undefined field20_0xc7;\n    struct duss_osal_task_handle_t *rc_monitor;\n    duss_event_client_handle_t event_client;\n    struct dummy_ui_ctx_t dummy_ui_ctx;\n    void *ctx;\n};\n\nstruct gs_wl_ctrl\n{\n    gs_media_cmd_chnl_t *mcc;\n    _Bool wl_start;\n    undefined field2_0x5;\n    undefined field3_0x6;\n    undefined field4_0x7;\n};\n\nstruct racing_debug_info_t\n{\n    uint8_t sdr_log_dump;\n    uint8_t vstream_dump;\n    uint8_t record;\n    uint32_t enc_sto_frm_drop_cnt;\n    uint32_t enc_lv_frm_drop_cnt;\n    uint32_t csi_frm_drop_cnt;\n    uint16_t channel_status;\n    uint16_t cam_frame_interval;\n    uint16_t outlier_frame_interval;\n    uint16_t outlier_frame_interval_cnt;\n    uint16_t cam_error;\n    uint16_t cam_ae_bv;\n    uint16_t cam_ae_dv;\n    uint32_t cam_ae_exp_time;\n    uint16_t cam_ae_iso;\n    uint16_t cam_awb_temp;\n    uint16_t cam_atm_value;\n    uint16_t cam_scene_mode;\n    uint16_t cam_snr;\n    uint16_t cam_ae_speed;\n    uint16_t cam_ltm_str;\n    uint16_t cam_saturation;\n    uint16_t cam_saturation_bias;\n    uint16_t cam_gamma;\n    uint16_t cam_snr_temp;\n    uint8_t cam_resolution;\n    uint8_t cam_angle;\n    uint8_t cam_mode;\n    uint8_t cam_ev;\n    uint8_t cam_satur;\n    uint8_t cam_white_balance;\n    int32_t shtl_ddr_temp;\n    int32_t shtl_acpu_temp;\n    int32_t shtl_modem_temp;\n    int32_t shtl_media_temp;\n    int32_t shtl_omc_temp;\n    uint8_t shtl_cp_report;\n    uint8_t shtl_cp_seq;\n    char shtl_ap_ver[128];\n    char shtl_board_sn[16];\n    uint8_t uav_rec_mode;\n    uint8_t uav_low_power_mode;\n    uint8_t uav_power_policy;\n    uint8_t HDVT_pref;\n    uint8_t enc_roi;\n    uint8_t sbus_mode;\n    char shtl_hardware_version[16];\n    uint8_t camera_type;\n};\n\nstruct gs_debug_ctrl\n{\n    struct __gs_info *gs_info;\n    product_shm_info_t *pdt_shm_va;\n    void *pdt_shm_align_va;\n    size_t pdt_shm_map_sz;\n    struct modem_shmem_info_t *plf_shm_va;\n    void *plf_shm_align_va;\n    size_t plf_shm_map_sz;\n    struct racing_debug_info_t rac_dbg_info;\n    undefined field8_0x116;\n    undefined field9_0x117;\n};\n\nstruct timeval\n{\n    __kernel_time_t tv_sec;\n    __kernel_suseconds_t tv_usec;\n};\n\nstruct pcm_config\n{\n    uint channels;\n    uint rate;\n    uint period_size;\n    uint period_count;\n    enum pcm_format format;\n    enum pcm_tstamp tstamp_type;\n    uint start_threshold;\n    uint stop_threshold;\n    uint silence_threshold;\n    uint silence_size;\n    int avail_min;\n};\n\nstruct gs_aout\n{\n    struct pcm_config config;\n    struct pcm *pcm_device;\n    uint32_t buffer_size;\n};\n\nstruct gs_lv_src\n{\n    int lv_dmi_fd;\n    _Bool force_stop;\n    undefined field2_0x5;\n    undefined field3_0x6;\n    undefined field4_0x7;\n    gs_media_cmd_chnl_t *mcc;\n    struct duss_osal_task_handle_t *lpc_ctrl_task;\n    _Bool lpc_ctrl_task_quit;\n    undefined field8_0x11;\n    undefined field9_0x12;\n    undefined field10_0x13;\n    struct duss_osal_msgq_handle_t *lpc_ctrl_queue;\n    struct duss_osal_task_handle_t *lpc_data_task;\n    _Bool lpc_data_task_quit;\n    _Bool lpc_data_eos;\n    undefined field15_0x1e;\n    undefined field16_0x1f;\n    gs_lv_csm_t *csm;\n};\n\nstruct gs_user_json_root\n{\n    struct cJSON *user_file_json_root;\n    struct cJSON *device_sn_json_root;\n};\n\nstruct gs_video_channel\n{\n    int (*start)(struct gs_video_channel *);\n    int (*stop)(struct gs_video_channel *);\n    _Bool (*handle_message)(struct gs_video_channel *, gs_video_channel_message_t *);\n    _Bool (*update_channel)(struct gs_video_channel *);\n    struct gs_video_channel_manager *chnl_mgr;\n    _Bool ready;\n    _Bool enable;\n    _Bool lazy_stop;\n    _Bool force_switch;\n    gs_main_channel_id_t id;\n    gs_sub_channel_id_t sub_id;\n    char *name;\n    _Bool sub_chnl_enable[6];\n    undefined field13_0x2a;\n    undefined field14_0x2b;\n};\n\nstruct gs_wl_channel\n{\n    gs_video_channel_t base;\n    _Bool wl_link;\n    uint8_t cam_workmode;\n    uint8_t cam_pbmode;\n    uint8_t cam_model;\n};\n\nstruct gs_local_playback_channel\n{\n    gs_video_channel_t base;\n    char video_path[256];\n    char audio_path[256];\n};\n\nstruct gs_csi_channel\n{\n    gs_video_channel_t base;\n    _Bool hdmi_plugin;\n    undefined field2_0x2d;\n    undefined field3_0x2e;\n    undefined field4_0x2f;\n};\n\nstruct gs_rc_setting_channel\n{\n    gs_video_channel_t base;\n};\n\nstruct gs_non_video_channel\n{\n    gs_video_channel_t base;\n};\n\nstruct gs_video_channel_id\n{\n    gs_main_channel_id_t main_chnl_id;\n    gs_sub_channel_id_t sub_chnl_id;\n};\n\nstruct gs_av_in_channel\n{\n    gs_video_channel_t base;\n    _Bool av_plugin;\n    undefined field2_0x2d;\n    undefined field3_0x2e;\n    undefined field4_0x2f;\n};\n\nstruct gs_video_channel_manager\n{\n    gs_wl_channel_t wl_chnl;\n    gs_local_playback_channel_t local_pb_chnl;\n    gs_av_in_channel_t av_in_chnl;\n    gs_rc_setting_channel_t rc_setting_chnl;\n    gs_csi_channel_t csi_chnl;\n    gs_non_video_channel_t non_video_chnl;\n    gs_video_channel_t *cur_chnl;\n    gs_video_channel_t *indexed_chnls[6];\n    gs_video_channel_t *prior_ordered_chnls[6];\n    gs_video_channel_t *pend_release_chnl;\n    gs_video_channel_id_t last_chnl_id;\n    struct __gs_info *gs_info;\n    gs_video_channel_switch_callback_t chnl_switch_cb;\n    void *chnl_switch_data;\n    gs_video_channel_push_callback_t chnl_push_cb;\n    void *chnl_push_data;\n    int8_t ignore_cam_push_cnt;\n    uint8_t cam_model;\n    uint8_t cam_workmode;\n    uint8_t cam_pbmode;\n    uint8_t racing_mode;\n    undefined field21_0x36d;\n    undefined field22_0x36e;\n    undefined field23_0x36f;\n    uint32_t listener_id;\n    struct duss_osal_task_handle_t *chnl_ctrl_task;\n    _Bool chnl_ctrl_task_quit;\n    undefined field27_0x379;\n    undefined field28_0x37a;\n    undefined field29_0x37b;\n    struct duss_osal_msgq_handle_t *chnl_ctrl_queue;\n    struct duss_osal_mutex_handle_t *chnl_ctrl_lock;\n};\n\nstruct duss_osal_mutex_attrib_t\n{\n    char *name;\n};\n\nstruct gs_buzzer_info\n{\n    struct duss_osal_mutex_handle_t *buzzer_mutex;\n    struct duss_osal_mutex_attrib_t buzzer_mutex_attr;\n    struct duss_osal_task_handle_t *buzzer_play_sound;\n    _Bool buzzer_play_sound_quit;\n    undefined field4_0xd;\n    undefined field5_0xe;\n    undefined field6_0xf;\n    int buzzer_enable_fd;\n    int buzzer_period_fd;\n    int buzzer_duty_fd;\n    _Bool buzzer_bat_enable;\n    undefined field11_0x1d;\n    undefined field12_0x1e;\n    undefined field13_0x1f;\n};\n\nstruct _DUSS_MSG_RC_BAT_INFO_t\n{\n    uint32_t bat_remain_value;\n    uint8_t bat_remain_ratio;\n    uint8_t bat_is_charging;\n};\n\nstruct glass_signal_quality_t\n{\n    uint8_t direction;\n    uint8_t signal_percent;\n    uint8_t energy1_uav_or_gnd;\n    uint8_t energy2_uav_or_gnd;\n};\n\nstruct _DUSS_MSG_RACING_PHY_CHECK_REQ_t\n{\n    uint8_t ant_check;\n    uint8_t reserved[2];\n};\n\nstruct _DUSS_MSG_RACING_PHY_CHECK_INFO_t\n{\n    uint8_t tx_ant_status;\n    uint8_t rx_ant_status;\n    uint8_t tx_ant_numbers;\n    uint8_t rx_ant_numbers;\n    uint8_t reserved;\n};\n\nstruct _DUSS_MSG_RACING_CHANNEL_SCAN_INFO_t\n{\n    uint8_t chnl_num;\n    uint8_t bw;\n    DUSS_MSG_RACING_CHANNEL_OCCUPIED_IPSD_t chn_info[16];\n    uint8_t flag;\n};\n\nstruct _DUSS_MSG_RACING_CHANNEL_SCAN_REQ_t\n{\n    uint8_t chnl_num;\n    uint8_t bw;\n    uint16_t freq_idx[16];\n};\n\nstruct gs_modem_link_listener\n{\n    void (*link_chg_cb)(void *, _Bool);\n    void *cb_ctx;\n};\n\nstruct _DUSS_MSG_RACING_CHANNEL_ROB_INFO_t\n{\n    uint8_t left_time;\n    uint8_t robber_name[7];\n};\n\nstruct gs_modem_ctrl\n{\n    int8_t gls_role;\n    uint8_t gls_count;\n    uint16_t conf_items[17];\n    struct duss_osal_mutex_handle_t *status_mutex_handle;\n    _Bool modem_monitor_quit;\n    undefined field5_0x29;\n    undefined field6_0x2a;\n    undefined field7_0x2b;\n    struct duss_osal_task_handle_t *modem_monitor;\n    uint8_t modem_state;\n    uint8_t sdr_role;\n    _Bool in_factory_mode;\n    _Bool in_aging_test;\n    _Bool flag_pairing_finished;\n    uint8_t last_modem_state;\n    uint8_t rob_flag;\n    undefined field16_0x37;\n    duss_event_client_handle_t event_client;\n    uint32_t seq_id;\n    gs_modem_link_listener_t listeners[8];\n    struct glass_signal_quality_t signal_quality;\n    DUSS_MSG_RACING_CHANNEL_SCAN_REQ_t scan_req;\n    DUSS_MSG_RACING_CHANNEL_SCAN_INFO_t scan_info;\n    DUSS_MSG_RACING_CHANNEL_ROB_INFO_t rob_info;\n    DUSS_MSG_RACING_PHY_CHECK_REQ_t check_req;\n    DUSS_MSG_RACING_PHY_CHECK_INFO_t check_info;\n    undefined field26_0x139;\n    undefined field27_0x13a;\n    undefined field28_0x13b;\n    enum gs_modem_scan_type_t modem_scan_type;\n    uint8_t area_id;\n    uint8_t nation_code[2];\n    uint8_t area_active_flag;\n    int dev_mem;\n    void *uncal_nvram_va;\n    void *uncal_nvram_align_va;\n    size_t uncal_nvram_map_sz;\n    struct glass_signal_quality_t rc_signal_quality;\n    uint16_t update_bandwidth;\n    _Bool power_level_expanded;\n    _Bool bw_pending;\n};\n\nstruct DUSS_MSG_EXT_FC_RTC\n{\n    uint16_t year;\n    uint8_t month;\n    uint8_t day;\n    uint8_t hours;\n    uint8_t minutes;\n    uint8_t seconds;\n    uint16_t millis;\n};\n\nstruct DUSS_MSG_EXT_FC_RATE\n{\n    uint8_t roll_rates;\n    uint8_t roll_expos;\n    uint8_t roll_s_rates;\n    uint8_t pitch_s_rates;\n    uint8_t yaw_s_rates;\n    uint8_t dyn_thr;\n    uint8_t thr_mid;\n    uint8_t thr_expos;\n    uint16_t tpa_bp;\n    uint8_t yaw_expos;\n    uint8_t yaw_rates;\n    uint8_t pitch_rates;\n    uint8_t pitch_expos;\n    uint8_t throttle_limit_type;\n    uint8_t throttle_limit_percent;\n    uint16_t rate_limit_roll;\n    uint16_t rate_limit_pitch;\n    uint16_t rate_limit_yaw;\n};\n\nstruct DUSS_MSG_EXT_FC_FILTER\n{\n    uint8_t gyro_lpf1;\n    uint16_t dterm_lpf1;\n    uint16_t yaw_lpf;\n    uint16_t gyro_no1;\n    uint16_t gyro_no1_cut;\n    uint16_t dterm_no1;\n    uint16_t dterm_no1_cut;\n    uint16_t gyro_no2;\n    uint16_t gyro_no2_cut;\n    uint8_t dterm_lpf1_type;\n    uint8_t gyro_hw_cut;\n    uint8_t gyro_hw32_cut;\n    uint16_t gyro_lpf1_alt;\n    uint16_t gyro_lpf2;\n    uint8_t gyro_lpf1_type;\n    uint8_t gyro_lpf2_type;\n    uint16_t dterm_lpf2;\n    uint8_t dterm_ft2_type;\n    uint16_t dyn_lpf_gyro_min_hz;\n    uint16_t dyn_lpf_gyro_max_hz;\n    uint16_t dyn_lpf_dterm_min_hz;\n    uint16_t dyn_lpf_dterm_max_hz;\n    uint8_t dyn_notch_range;\n    uint8_t dyn_notch_width_percent;\n    uint16_t dyn_notch_q;\n    uint16_t dyn_notch_min_hz;\n    uint8_t gyro_rpm_notch_harmonics;\n    uint8_t gyro_rpm_notch_min;\n};\n\nstruct DUSS_MSG_EXT_FC_AUX\n{\n    uint8_t aux_id;\n    uint8_t aux_index;\n    uint8_t start_step;\n    uint8_t end_step;\n};\n\nstruct DUSS_MSG_EXT_FC_BATTERY_STATE\n{\n    uint8_t cell;\n    uint16_t capacity;\n    uint8_t voltage;\n    uint16_t MAhDrawn;\n    uint16_t amperage;\n    uint8_t state;\n    uint16_t battVoltage;\n};\n\nstruct DUSS_MSG_EXT_FC_RC\n{\n    uint16_t chnl_1_v;\n    uint16_t chnl_2_v;\n    uint16_t chnl_3_v;\n    uint16_t chnl_4_v;\n    uint16_t chnl_5_v;\n    uint16_t chnl_6_v;\n    uint16_t chnl_7_v;\n    uint16_t chnl_8_v;\n    uint16_t chnl_9_v;\n    uint16_t chnl_10_v;\n    uint16_t chnl_11_v;\n    uint16_t chnl_12_v;\n    uint16_t chnl_13_v;\n    uint16_t chnl_14_v;\n    uint16_t chnl_15_v;\n    uint16_t chnl_16_v;\n    uint16_t chnl_17_v;\n    uint16_t chnl_18_v;\n};\n\nstruct DUSS_MSG_EXT_FC_MSP_STATUS\n{\n    uint16_t task_delta_time;\n    uint16_t i2c_error_counter;\n    uint16_t sensor_status;\n    uint32_t flight_flags_lsb;\n    uint8_t pid_index;\n    uint16_t sysAvgLoad;\n    uint8_t pid_count;\n    uint8_t rate_index;\n    uint8_t flight_count;\n    uint8_t arming_count;\n    uint32_t arming_flags;\n    uint8_t flight_flags_msb;\n};\n\nstruct DUSS_MSG_EXT_FC_ESC_DATA\n{\n    uint8_t temperature;\n    uint16_t rpm;\n};\n\nstruct DUSS_MSG_EXT_FC_OSD_CONFIG\n{\n    uint8_t osdFlags;\n    uint8_t videoSystems;\n    uint8_t units;\n    uint8_t rssi_alarm;\n    uint16_t cap_alarm;\n    uint8_t reserved;\n    uint8_t item_count;\n    uint16_t alt_alarm;\n    uint16_t item_position[100];\n    uint8_t stat_count;\n    uint8_t stat_state[100];\n    uint8_t timer_count;\n    uint16_t timer[10];\n    uint16_t enablewarnings;\n    uint8_t warning_count;\n    uint32_t warnings;\n    uint8_t profile_count;\n    uint8_t profile_index;\n    uint8_t stick_overlay;\n};\n\nstruct DUSS_MSG_EXT_FC_SERVO\n{\n    uint16_t min;\n    uint16_t max;\n    uint16_t middle;\n    uint8_t rate;\n    uint8_t ffchnl;\n    uint32_t reverse;\n};\n\nstruct DUSS_MSG_EXT_FC_VERSION\n{\n    uint8_t major;\n    uint8_t minor;\n    uint8_t patch_level;\n};\n\nstruct DUSS_MSG_EXT_FC_ADVANCED_PID\n{\n    uint16_t reserved1;\n    uint16_t reserved2;\n    uint16_t reserved3;\n    uint8_t reserved4;\n    uint8_t vbat_comp;\n    uint8_t feedForwardTrans;\n    uint8_t reserved5;\n    uint8_t reserved6;\n    uint8_t reserved7;\n    uint8_t reserved8;\n    uint16_t rateAcelLimit;\n    uint16_t yawrateAcelLimit;\n    uint8_t levelAngleLimit;\n    uint8_t reserved9;\n    uint16_t ThrottleThreshold;\n    uint16_t AcceleratorGain;\n    uint16_t reserved10;\n    uint8_t rotation;\n    uint8_t smartFF;\n    uint8_t relax;\n    uint8_t relax_type;\n    uint8_t abs_control_gain;\n    uint8_t throttle_boost;\n    uint8_t acro_trainer_angle_limit;\n    uint16_t RollFF;\n    uint16_t PitchFF;\n    uint16_t YawFF;\n    uint8_t ag_mode;\n    uint8_t d_min_roll;\n    uint8_t d_min_pitch;\n    uint8_t d_min_yaw;\n    uint8_t d_min_gain;\n    uint8_t d_min_advance;\n    uint8_t use_integrated_yaw;\n    uint8_t integrated_yaw_relax;\n    uint8_t iterm_relax_cutoff;\n};\n\nstruct DUSS_MSG_FC_RACING_DRONE_OSD_PUSH\n{\n    uint8_t GPS_3D_fix;\n    uint8_t GPS_numSat;\n    int32_t Lattitude;\n    int32_t Longtitude;\n    uint16_t GPS_altitude;\n    uint16_t GPS_speed;\n    uint16_t GPS_distance_home;\n    int16_t GPS_direction_home;\n    int16_t angx;\n    int16_t angy;\n    int16_t heading;\n    int32_t estAlt;\n    int16_t vario;\n    uint8_t vbat;\n    uint16_t intPowerMeterSum;\n    uint16_t rssi;\n    uint16_t amperage;\n};\n\nstruct DUSS_MSG_EXT_FC_PID\n{\n    uint8_t roll_p;\n    uint8_t roll_i;\n    uint8_t roll_d;\n    uint8_t pitch_p;\n    uint8_t pitch_i;\n    uint8_t pitch_d;\n    uint8_t yaw_p;\n    uint8_t yaw_i;\n    uint8_t yaw_d;\n    uint8_t angle_str;\n    uint8_t horizon_str;\n    uint8_t horizon_trans;\n    uint8_t mag_p;\n    uint8_t mag_i;\n    uint8_t mag_d;\n};\n\nstruct gs_ext_fc\n{\n    uint8_t requestType;\n    DUSS_MSG_EXT_FC_PID_t pid;\n    DUSS_MSG_EXT_FC_AUX_t aux[20];\n    DUSS_MSG_EXT_FC_RATE_t rate;\n    DUSS_MSG_EXT_FC_SERVO_t servo[8];\n    DUSS_MSG_EXT_FC_FILTER_t filter;\n    DUSS_MSG_EXT_FC_ADVANCED_PID_t pid_adv;\n    DUSS_MSG_EXT_FC_MSP_STATUS_t msp_status;\n    DUSS_MSG_EXT_FC_RC_t rc;\n    DUSS_MSG_EXT_FC_BATTERY_STATE_t batt;\n    DUSS_MSG_EXT_FC_OSD_CONFIG_t osd_config;\n    DUSS_MSG_EXT_FC_ESC_DATA_t esc_data[4];\n    DUSS_MSG_EXT_FC_RTC_t fc_rtc;\n    char name[17];\n    DUSS_MSG_EXT_FC_VERSION_t fc_version;\n    DUSS_MSG_FC_RACING_DRONE_OSD_PUSH_t osd_info;\n    undefined field16_0x31b;\n    enum GS_EXT_FC_OSD_STATUS osd_status;\n    struct duss_osal_task_handle_t *get_osd_config;\n    _Bool get_osd_quit;\n    undefined field20_0x325;\n    undefined field21_0x326;\n    undefined field22_0x327;\n};\n\nstruct _DUSS_MSG_HISTOGRAM_t\n{\n    uint8_t Op : 1;\n    uint8_t Reserved : 7;\n};\n\nstruct anon_struct_conflict2349\n{\n    uint16_t DataDisp : 1;\n    uint16_t PertureDisp : 1;\n    uint16_t ShutterSpeedDisp : 1;\n    uint16_t ISODisp : 1;\n    uint16_t EvBiasDisp : 1;\n    uint16_t SharpnessDisp : 1;\n    uint16_t ContrastDisp : 1;\n    uint16_t SaturationDisp : 1;\n    uint16_t Res : 8;\n};\n\nunion anon_union_conflict23e3_for_u\n{\n    struct anon_struct_conflict2349 bits;\n    uint16_t Value;\n};\n\nstruct _DUSS_MSG_PHOTO_OSD_PARA_\n{\n    union anon_union_conflict23e3_for_u u;\n};\n\nstruct anon_struct_conflict242a\n{\n    uint32_t DataDisp : 1;\n    uint32_t PertureDisp : 1;\n    uint32_t ShutterSpeedDisp : 1;\n    uint32_t ISODisp : 1;\n    uint32_t AeModeDisp : 1;\n    uint32_t FlashLightDisp : 1;\n    uint32_t WbDisp : 1;\n    uint32_t EvBiasDisp : 1;\n    uint32_t SharpnessDisp : 1;\n    uint32_t ContrastDisp : 1;\n    uint32_t SaturationDisp : 1;\n    uint32_t CapModeDisp : 1;\n    uint32_t PhoSizeDisp : 1;\n    uint32_t PhoVideoQualityDisp : 1;\n    uint32_t HistogramsDisp : 1;\n    uint32_t BatteryPowerDisp : 1;\n    uint32_t Res : 16;\n};\n\nunion anon_union_conflict2544_for_u\n{\n    struct anon_struct_conflict242a bits;\n    uint16_t Value;\n};\n\nstruct _DUSS_MSG_PREVIEW_OSD_PARA_t\n{\n    union anon_union_conflict2544_for_u u;\n};\n\nstruct _DUSS_MSG_QUICKVIEW_t\n{\n    uint8_t Time : 7;\n    uint8_t Op : 1;\n};\n\nstruct _DUSS_MSG_CAMERA_AUDIO_STATUS_t\n{\n    uint8_t reversed : 6;\n    uint8_t mic_opened : 1;\n    uint8_t speaker_opened : 1;\n    uint8_t speaker_audio_volumn;\n};\n\nstruct _DUSS_MSG_F_INDEX_MODE_t\n{\n    uint8_t Mode;\n};\n\nstruct _DUSS_MSG_CAM_STATE_t\n{\n    uint32_t WifiCon : 1;\n    uint32_t UsbCon : 1;\n    uint32_t TimeSync : 1;\n    uint32_t Capture : 3;\n    uint32_t Record : 2;\n    uint32_t SensorErr : 1;\n    uint32_t SdInserted : 1;\n    uint32_t SdStatus : 4;\n    uint32_t FwUpgrading : 1;\n    uint32_t FwUpgradErr : 2;\n    uint32_t OverHeat : 1;\n    uint32_t CapForbit : 1;\n    uint32_t Storage : 1;\n    uint32_t ContiCap : 1;\n    uint32_t Hdmi : 1;\n    uint32_t Encrypt : 2;\n    uint32_t FileSync : 1;\n    uint32_t RCBtmForbid : 1;\n    uint32_t InFocus : 1;\n    uint32_t gimbal_state : 1;\n    uint32_t is_tracking : 1;\n    uint32_t is_hyperlapse : 1;\n    uint32_t Reserved : 2;\n};\n\nstruct _DUSS_MSG_SYSTEM_STATE_t\n{\n    DUSS_MSG_CAM_STATE_t CamState;\n    uint8_t WorkMode;\n};\n\nstruct _DUSS_MSG_HYPERLAPSE_LIVEVIEW_MARGIN_t\n{\n    float x;\n    float y;\n};\n\nstruct _DUSS_MSG_CAMERA_STATUS_PUSH_t\n{\n    DUSS_MSG_SYSTEM_STATE_t CameraStatus;\n    uint32_t Capacity;\n    uint32_t SpareCap;\n    uint32_t RemainShots;\n    uint32_t RemainTime;\n    DUSS_MSG_F_INDEX_MODE_t FileIndexMode;\n    DUSS_MSG_QUICKVIEW_t PhotoQuickViewPara;\n    DUSS_MSG_PHOTO_OSD_PARA_t PhotoOSDPara;\n    DUSS_MSG_PREVIEW_OSD_PARA_t PreviewOSDPara;\n    uint16_t RecordTime;\n    uint8_t CapCapacityNum;\n    DUSS_MSG_HISTOGRAM_t Histogram;\n    uint8_t CameraModel;\n    uint16_t CurrentRecordingTime;\n    uint8_t ProtocolType;\n    uint8_t RawSupport;\n    uint8_t LcdFormat;\n    uint8_t Reserved;\n    uint8_t CropMode;\n    uint8_t NdFilter;\n    uint32_t ErrCode;\n    uint8_t mic_audio_volumn;\n    uint8_t camera_advance_mode;\n    uint8_t StreamQuality;\n    DUSS_MSG_CAMERA_AUDIO_STATUS_t audio_status;\n    DUSS_MSG_HYPERLAPSE_LIVEVIEW_MARGIN_t HyperlapseMargin;\n};\n\nstruct duss_list_head\n{\n    struct duss_list_head *next;\n    struct duss_list_head *prev;\n};\n\nstruct gs_storage_listener\n{\n    void *ctx;\n    int (*event_cb)(void *, storage_event_t);\n};\n\nstruct gs_lv_rec_ctrl\n{\n    uint8_t *lv_rec_state;\n    gs_lv_src_t *lv_src;\n    gs_lv_csm_t lv_csm;\n    gs_storage_listener_t storage_listener;\n    struct duss_osal_mutex_handle_t *list_mutex;\n    struct duss_list_head frame_list;\n    int num_in_list;\n    int frames_received;\n    _Bool b_wrf_running;\n    undefined field9_0x3d;\n    undefined field10_0x3e;\n    undefined field11_0x3f;\n    int frames_write;\n    struct duss_osal_task_handle_t *task_wrf;\n};\n\nstruct gs_avin_us\n{\n    uint8_t brightness;\n    uint8_t saturation;\n    undefined field2_0x2;\n    undefined field3_0x3;\n    int dev_mem;\n    void *uncal_nvram_va;\n    void *uncal_nvram_align_va;\n    size_t uncal_nvram_map_sz;\n};\n\nstruct gs_av_in_ctrl\n{\n    gs_media_cmd_chnl_t *mcc;\n    _Bool avin_start;\n    undefined field2_0x5;\n    undefined field3_0x6;\n    undefined field4_0x7;\n    gs_avin_us_t avin_us;\n};\n\nstruct gs_camera_cmd_ctrl_t\n{\n    struct duss_osal_task_handle_t *cam_cmd_task;\n    struct duss_osal_msgq_handle_t *cam_cmd_queue;\n    _Bool cam_cmd_task_quit;\n    undefined field3_0x9;\n    undefined field4_0xa;\n    undefined field5_0xb;\n    duss_event_client_handle_t event_client;\n    _Bool init;\n    _Bool set_best_liveview;\n    uint8_t lv_format;\n    undefined field10_0x13;\n    struct duss_osal_task_handle_t *cam_monitor_task;\n    _Bool cam_monitor_task_quit;\n    undefined field13_0x19;\n    undefined field14_0x1a;\n    undefined field15_0x1b;\n    void *ctx;\n};\n\nstruct gs_bl\n{\n    duss_hal_obj_handle_t i2c_obj[2];\n    uint8_t brightness_value;\n    undefined field2_0x9;\n    undefined field3_0xa;\n    undefined field4_0xb;\n    float lr_ratio;\n    uint8_t mode;\n    _Bool b_quit_monitor;\n    undefined field8_0x12;\n    undefined field9_0x13;\n    struct duss_osal_task_handle_t *lcd_monitor_task;\n};\n\nstruct _DUSS_MSG_RC_MS_LINK_STATUS\n{\n    uint8_t pair_role;\n    uint8_t master_link_stat : 4;\n    uint8_t master_device_type : 4;\n    uint8_t slave_link_stat : 4;\n    uint8_t slave_device_type : 4;\n    uint8_t coach_role;\n    uint8_t double_rc_stat;\n};\n\nstruct duss_osal_timer_attrib_t\n{\n    char *name;\n    duss_osal_timer_entry_t entry;\n    void *param;\n    uint32_t timeout_us;\n    int32_t repeat_times;\n    enum duss_osal_priority_t priority;\n};\n\nstruct gs_fan_info\n{\n    struct duss_osal_task_handle_t *fan_adjust_task;\n    _Bool fan_adjust_task_quit;\n    undefined field2_0x5;\n    undefined field3_0x6;\n    undefined field4_0x7;\n    int fan_pwm_period;\n    int fan_pwm_duty;\n    enum gs_fan_level_t fan_pwm_level;\n    _Bool fan_enable;\n    undefined field9_0x15;\n    undefined field10_0x16;\n    undefined field11_0x17;\n};\n\nstruct __gs_use_times_info\n{\n    uint8_t magic[2];\n    _Bool flag;\n    uint32_t times;\n};\n\nstruct gs_usb_listener\n{\n    void *ctx;\n    int (*event_cb)(void *, usb_event_t);\n};\n\nstruct gs_sd_listener\n{\n    void *ctx;\n    int (*event_cb)(void *, sd_event_t);\n};\n\nstruct gs_lv_transcode\n{\n    ion_info_t *ion_info;\n    struct duss_osal_mutex_handle_t *rec_mutex;\n    record_state_t cur_state;\n    record_state_t tgt_state;\n    _Bool b_rec_running;\n    undefined field5_0x11;\n    undefined field6_0x12;\n    undefined field7_0x13;\n    struct duss_osal_task_handle_t *rec_thread;\n    uint8_t *lv_rec_state;\n    gs_local_sd_info_t *sd_info;\n    gs_sd_listener_t sd_listener;\n    gs_usb_listener_t usb_listener;\n    gs_media_cmd_chnl_t *mcc;\n    int dmi_data_fd;\n    int file_idx;\n    char file_name[128];\n    struct AVCodecContext *avcodec_ctx;\n    p1_muxer_handle_t muxer;\n    record_mode_t rec_mode;\n    int video_stream_index;\n    undefined field21_0xcc;\n    undefined field22_0xcd;\n    undefined field23_0xce;\n    undefined field24_0xcf;\n    int64_t cur_rec_time;\n    char *sps_buf;\n    uint32_t sps_len;\n    struct duss_osal_mutex_handle_t *video_list_mutex;\n    struct duss_list_head video_list;\n    int num_frames;\n    _Bool b_running;\n    _Bool b_eos;\n    undefined field33_0xf2;\n    undefined field34_0xf3;\n    struct duss_osal_task_handle_t *task_rx;\n    uint32_t frames_received;\n    uint32_t frames_write;\n    uint32_t last_key_frm_idx;\n    uint32_t last_frame_idx;\n    _Bool b_wrf_running;\n    undefined field41_0x109;\n    undefined field42_0x10a;\n    undefined field43_0x10b;\n    FILE *p_file;\n    struct duss_osal_task_handle_t *task_wrf;\n    int first_idx_in_file;\n    _Bool b_pending_new_file;\n    _Bool b_avin_recording;\n    undefined field49_0x11a;\n    undefined field50_0x11b;\n    undefined field51_0x11c;\n    undefined field52_0x11d;\n    undefined field53_0x11e;\n    undefined field54_0x11f;\n    int64_t cur_rec_time_ms;\n    char srt_file_name[128];\n    _Bool b_srt_record_running;\n    _Bool b_srt_switch_new_file;\n    undefined field59_0x1aa;\n    undefined field60_0x1ab;\n    FILE *p_srt_file;\n    struct duss_osal_task_handle_t *task_srt_record;\n    undefined field63_0x1b4;\n    undefined field64_0x1b5;\n    undefined field65_0x1b6;\n    undefined field66_0x1b7;\n};\n\nstruct __gs_info\n{\n    duss_event_client_handle_t event_client;\n    duss_hal_obj_handle_t vmem_obj;\n    duss_hal_obj_handle_t gpio_obj;\n    int32_t dev_mem;\n    gs_debug_ctrl_t dbg_ctrl;\n    struct duss_osal_task_handle_t *sdr_status_listen_task;\n    _Bool b_sdr_status_quit;\n    undefined field7_0x12d;\n    undefined field8_0x12e;\n    undefined field9_0x12f;\n    gs_local_sd_info_t sd_info;\n    struct duss_osal_timer_handle_t *sd_monitor_timer;\n    DUSS_MSG_RC_BAT_INFO_t bat_info;\n    undefined field13_0x16a;\n    undefined field14_0x16b;\n    gs_battery_info_t batt_info;\n    uint8_t current_uav_rc_sdr_mode;\n    undefined field17_0x185;\n    undefined field18_0x186;\n    undefined field19_0x187;\n    gs_modem_ctrl_t modem_ctrl;\n    DUSS_MSG_RC_MS_LINK_STATUS_t ms_link_stat;\n    _Bool b_active_quit;\n    _Bool b_rc_mp_state_got;\n    _Bool b_rc_active_get_pending;\n    _Bool b_uav_mp_state_got;\n    _Bool b_uav_active_get_pending;\n    uint8_t uav_mp_state;\n    uint8_t uav_active_state;\n    uint8_t gls_active_state;\n    undefined field30_0x2f1;\n    undefined field31_0x2f2;\n    undefined field32_0x2f3;\n    struct duss_osal_task_handle_t *get_activate_task;\n    gs_use_times_info_t use_times_before_active;\n    undefined field35_0x2ff;\n    gs_rc_ctrl_t rc_ctrl;\n    struct keys_pack_to_racing_glass_t key_state;\n    _Bool key_flag;\n    undefined field39_0x3f7;\n    struct duss_osal_timer_handle_t *keyscan_task_handle;\n    struct duss_osal_timer_attrib_t keyscan_task_attrib;\n    struct duss_osal_timer_handle_t *detect_taking_off_task_handle;\n    struct duss_osal_timer_attrib_t detect_taking_off_task_attrib;\n    struct pack_for_factory_test_t factory_test_key_info;\n    gs_buzzer_info_t buzzer_info;\n    gs_fan_info_t fan_info;\n    uint8_t cam_model;\n    uint8_t cam_work_mode;\n    _Bool check_cam_work_mode;\n    _Bool cam_is_pb;\n    gs_common_cmd_ctrl_t cmn_cmd_ctrl;\n    struct gs_camera_cmd_ctrl_t cam_cmd_ctrl;\n    DUSS_MSG_CAMERA_STATUS_PUSH_t uav_cam_stat;\n    undefined field54_0x50b;\n    gs_video_channel_manager_t chnl_mgr;\n    gs_wl_ctrl_t wl_ctrl;\n    gs_av_in_ctrl_t avin_ctrl;\n    vdec_local_player_t *player;\n    metadata_retriever_t metad_rt;\n    uint8_t lv_rec_state;\n    undefined field61_0x8d1;\n    undefined field62_0x8d2;\n    undefined field63_0x8d3;\n    gs_lv_src_t lv_src;\n    gs_lv_rec_ctrl_t lv_rec_ctrl;\n    gs_usb_gadget_vt_t usb_vt;\n    gs_lv_transcode_t lv_transcode;\n    gs_aout_t gs_aout;\n    gs_audio_wl_t audio_wl;\n    gs_media_cmd_chnl_t media_cmd_chnl;\n    gs_bl_t gs_bl;\n    struct timeval disarm_start;\n    uint8_t disarm_flag;\n    uint8_t low_power_mode;\n    undefined field75_0xb9a;\n    undefined field76_0xb9b;\n    uint32_t rtc_listener_id;\n    struct duss_osal_timer_handle_t *rtc_brdcst_timer;\n    struct duss_osal_timer_handle_t *lcdc_blending_check_timer;\n    gs_gui_t gui;\n    gs_ext_fc_t ext_fc;\n    gs_shram_t pdt_shram;\n    gs_shram_t plf_shram;\n    char user_data_file_name[64];\n    gs_queue_t user_data_queue;\n    struct duss_osal_timer_handle_t *user_data_update_timer;\n    struct duss_osal_task_handle_t *user_data_sender_task;\n    struct duss_osal_task_handle_t *device_sn_check_task;\n    _Bool is_pc_connect;\n    uint8_t user_privacy_setting;\n    uint16_t user_vt_sn_id;\n    uint16_t user_rc_sn_id;\n    uint8_t user_country_code[2];\n    _Bool b_sender_task_quit;\n    _Bool b_sn_check_task_quit;\n    gs_user_json_root_t json_root_info;\n    undefined field97_0xf9a;\n    undefined field98_0xf9b;\n    struct duss_osal_mutex_handle_t *user_queue_mutex;\n    gs_avin_test_ctrl_t avin_test_ctrl;\n    _Bool is_speaker_test_start;\n    undefined field102_0xfa9;\n    undefined field103_0xfaa;\n    undefined field104_0xfab;\n    struct duss_osal_mutex_handle_t *test_status_mutex;\n    uint8_t uav_power_status;\n    uint8_t arm_flag_from_uav;\n    uint8_t exit_flag;\n    undefined field109_0xfb3;\n    gs_watermarker_ctrl_t watermarker_ctrl;\n    undefined field111_0xfcc;\n    undefined field112_0xfcd;\n    undefined field113_0xfce;\n    undefined field114_0xfcf;\n};\n\nstruct ip_channel_addr\n{\n    char *ifx;\n    uint16_t loc_port;\n    undefined field2_0x6;\n    undefined field3_0x7;\n    char *address;\n    uint16_t rmt_port;\n    undefined field6_0xe;\n    undefined field7_0xf;\n};\n\nstruct pthread_cond_t\n{\n    int32_t __private[1];\n};\n\nstruct AVFrac\n{\n    int64_t val;\n    int64_t num;\n    int64_t den;\n};\n\nstruct duss_hal_storage_info\n{\n    char id[64];\n    char fs[8];\n    char path[256];\n    char state;\n    undefined field4_0x149;\n    undefined field5_0x14a;\n    undefined field6_0x14b;\n    int speed;\n    int uhs_grade;\n    int capacity;\n    int readonly;\n    int block_size;\n};\n\nstruct AVIndexEntry\n{\n    int64_t pos;\n    int64_t timestamp;\n    int flags : 2;\n    int size : 30;\n    int min_distance;\n};\n\nstruct AVBuffer\n{\n};\n\nstruct AVRational\n{\n    int num;\n    int den;\n};\n\nstruct AVChapter\n{\n    int id;\n    struct AVRational time_base;\n    undefined field2_0xc;\n    undefined field3_0xd;\n    undefined field4_0xe;\n    undefined field5_0xf;\n    int64_t start;\n    int64_t end;\n    struct AVDictionary *metadata;\n    undefined field9_0x24;\n    undefined field10_0x25;\n    undefined field11_0x26;\n    undefined field12_0x27;\n};\n\nstruct gs_gui_event_t\n{\n    gs_gui_event_type_t type;\n    gs_gui_event_symbol_t symbol;\n    float posx;\n    float posy;\n    float dx;\n    float dy;\n    float yaw;\n    float pitch;\n    float roll;\n};\n\nstruct _DUSS_MSG_V_CRTL_t\n{\n    uint8_t Op;\n    uint32_t time_offset;\n};\n\nstruct pthread_mutex_t\n{\n    int32_t __private[1];\n};\n\nstruct duss_osal_event_attrib_t\n{\n    char *name;\n};\n\nstruct duss_osal_event_handle_t\n{\n    struct duss_osal_event_attrib_t attrib;\n    struct pthread_mutex_t mutex;\n    struct pthread_cond_t cond;\n    uint32_t data;\n    int32_t flag;\n};\n\nstruct modem_ops\n{\n    int (*pairing_control)(void *, uint32_t);\n    int (*set_pairing_role)(void *, uint32_t);\n    int (*get_link_stat)(void *, enum gs_link_stat_t *);\n    int (*get_bandwidth)(void *, uint16_t *);\n    int (*set_bandwidth)(void *, uint16_t);\n    int (*get_chnl)(void *, uint16_t *);\n    int (*set_chnl)(void *, uint16_t);\n    int (*rob_chnl)(void *, uint16_t);\n    int (*rob_chnl_cancel)(void *);\n    int (*get_rob_left_time)(void *, uint8_t *);\n    int (*get_rob_flag)(void *, uint8_t *);\n    int (*clear_rob_flag)(void *);\n    int (*set_public_chnl)(void *);\n    int (*is_drone_broadcast)(void *, uint16_t *);\n    int (*enable_drone_broadcast)(void *, uint16_t);\n    int (*listen_broadcast)(void *, uint16_t);\n    int (*is_silent)(void *, uint16_t *);\n    int (*set_chnl_scan_info)(void *, uint8_t, uint8_t);\n    int (*scan_chnl_x)(void *, uint8_t, uint8_t);\n    int (*check_ant_status)(void *);\n    int (*if_new_chnl_scan_info)(void *, uint8_t *);\n    int (*if_new_phy_check_info)(void *, uint8_t *);\n    int (*if_update_bandwidth_pending)(void *, uint8_t *);\n    int (*clear_scan_flag)(void *);\n    int (*clear_check_flag)(void *);\n    int (*get_chnl_scan_info)(void *, uint8_t, uint8_t *, uint8_t *, uint32_t *);\n    int (*get_ant_status)(void *, uint8_t *, uint8_t *);\n    int (*get_dbg_mcs)(void *, uint16_t *);\n    int (*set_dbg_mcs)(void *, uint16_t);\n    int (*get_dbg_harq)(void *, uint16_t *);\n    int (*set_dbg_harq)(void *, uint16_t);\n    int (*get_dbg_codec_rate)(void *, uint16_t *);\n    int (*set_dbg_codec_rate)(void *, uint16_t);\n    int (*get_signal_quality)(void *, struct glass_signal_quality_t *);\n    int (*get_rc_signal_quality)(void *, struct glass_signal_quality_t *);\n    int (*set_bandwidth_mode)(void *, uint16_t);\n    int (*change_silent_mode)(void *, uint8_t);\n    int (*get_chnl_cnt)(void *, uint16_t *);\n    int (*get_freq_by_index)(void *, uint16_t *, uint16_t);\n    int (*get_scan_type)(void *, uint8_t *);\n    int (*get_wl_area_id)(void *, uint8_t *);\n    int (*get_pairing_finish_flag)(void *, uint8_t *);\n    int (*check_conf_items)(void *);\n};\n\nstruct _UAV_RECORD_MODE_t\n{\n    uint8_t type;\n    uint8_t value;\n};\n\nstruct AVFrameSideData\n{\n    enum AVFrameSideDataType type;\n    uint8_t *data;\n    int size;\n    struct AVDictionary *metadata;\n};\n\nstruct _DUSS_MSG_APERTURE_t\n{\n    uint16_t Value;\n};\n\nstruct _DUSS_MSG_VIDEO_STANDARD_t\n{\n    uint8_t Value;\n};\n\nstruct AVCodecParser\n{\n    int codec_ids[5];\n    int priv_data_size;\n    int (*parser_init)(struct AVCodecParserContext *);\n    int (*parser_parse)(struct AVCodecParserContext *, struct AVCodecContext *, uint8_t **, int *, uint8_t *, int);\n    void (*parser_close)(struct AVCodecParserContext *);\n    int (*split)(struct AVCodecContext *, uint8_t *, int);\n    struct AVCodecParser *next;\n};\n\nstruct hpi_channel\n{\n    __u32 priority;\n    __u8 major_id : 4;\n    __u8 minor_id : 4;\n    undefined field3_0x5;\n    __u16 order;\n};\n\nstruct hpi_channel_addr\n{\n    char *obj_name;\n    duss_hal_hpi_config_t cfg;\n};\n\nstruct _DUSS_MSG_WORKMODE_t\n{\n    uint8_t WorkMode;\n};\n\nstruct timespec\n{\n    __kernel_time_t tv_sec;\n    long tv_nsec;\n};\n\nstruct ext_fc_ops\n{\n    int (*get_craft_name)(void *, char *);\n    int (*get_pid_profile_index)(void *, uint8_t *);\n    int (*get_rate_profile_index)(void *, uint8_t *);\n    int (*get_osd_profile_index)(void *, uint8_t *);\n    int (*select_file)(void *, uint8_t, uint8_t);\n    int (*copy_file)(void *, uint8_t, uint8_t, uint8_t);\n    int (*write_eeprom)(void *);\n    int (*get_rc_channel)(void *, uint8_t, uint16_t *);\n    int (*get_aux_id)(void *, uint8_t *, uint8_t, uint8_t *, uint8_t *);\n    int (*get_aux_mode)(void *, uint8_t, uint8_t *, uint8_t *, uint8_t *);\n    int (*set_aux_mode)(void *, uint8_t, uint8_t, uint8_t, uint8_t);\n    int (*set_request_type)(void *, uint8_t);\n    int (*get_request_type)(void *, uint8_t *);\n    int (*get_ag_gain)(void *, uint16_t *);\n    int (*set_ag_gain)(void *, uint16_t);\n    int (*get_ag_thr)(void *, uint16_t *);\n    int (*set_ag_thr)(void *, uint16_t);\n    int (*get_thr_boost)(void *, uint8_t *);\n    int (*set_thr_boost)(void *, uint8_t);\n    int (*get_ff_trans)(void *, uint8_t *);\n    int (*set_ff_trans)(void *, uint8_t);\n    int (*get_filter)(void *, uint8_t, uint16_t *);\n    int (*set_filter)(void *, uint8_t, uint16_t);\n    int (*get_rate)(void *, uint8_t, uint8_t *);\n    int (*set_rate)(void *, uint8_t, uint8_t);\n    int (*get_super_rate)(void *, uint8_t, uint8_t *);\n    int (*set_super_rate)(void *, uint8_t, uint8_t);\n    int (*get_expo)(void *, uint8_t, uint8_t *);\n    int (*set_expo)(void *, uint8_t, uint8_t);\n    int (*get_dyn_thr)(void *, uint8_t *);\n    int (*set_dyn_thr)(void *, uint8_t);\n    int (*get_thr_mid)(void *, uint8_t *);\n    int (*set_thr_mid)(void *, uint8_t);\n    int (*get_thr_expo)(void *, uint8_t *);\n    int (*set_thr_expo)(void *, uint8_t);\n    int (*get_tpa)(void *, uint16_t *);\n    int (*set_tpa)(void *, uint16_t);\n    int (*get_pid)(void *, uint8_t, uint8_t *);\n    int (*set_pid)(void *, uint8_t, uint8_t);\n    int (*get_pid_f)(void *, uint8_t, uint16_t *);\n    int (*set_pid_f)(void *, uint8_t, uint16_t);\n    int (*get_battery_state)(void *, enum batteryState_e *);\n    int (*get_battery_voltage)(void *, uint16_t *);\n    int (*get_battery_avg_voltage)(void *, uint16_t *);\n    int (*get_battery_amperage)(void *, uint16_t *);\n    int (*get_battery_mah)(void *, uint16_t *);\n    int (*get_battery_usage)(void *, uint16_t *);\n    int (*get_battery_power)(void *, uint16_t *);\n    int (*set_arming_disabled)(void *, uint8_t);\n    int (*get_arming_disabled)(void *, uint32_t *);\n    int (*get_fc_version)(void *, uint8_t *, uint8_t *, uint8_t *);\n    int (*set_sbus_mode)(void *, uint8_t);\n    int (*get_racing_osd_info)(void *, DUSS_MSG_FC_RACING_DRONE_OSD_PUSH_t *);\n    int (*get_osd_position)(void *, int, _Bool *, int *, int *);\n    int (*get_units)(void *, uint8_t *);\n    int (*get_fly_mode)(void *, uint16_t *);\n    int (*get_rtc_date)(void *, DUSS_MSG_EXT_FC_RTC_t *);\n    int (*get_esc_temperature)(void *, uint8_t *);\n};\n\nstruct duss_hal_usbacc_config_t\n{\n    duss_usbacc_port_t port;\n};\n\nstruct usbacc_channel_addr\n{\n    char *obj_name;\n    struct duss_hal_usbacc_config_t cfg;\n    undefined field2_0x6;\n    undefined field3_0x7;\n};\n\nstruct AVCodecContext\n{\n    struct AVClass *av_class;\n    int log_level_offset;\n    enum AVMediaType codec_type;\n    struct AVCodec *codec;\n    char codec_name[32];\n    enum AVCodecID codec_id;\n    uint codec_tag;\n    uint stream_codec_tag;\n    void *priv_data;\n    struct AVCodecInternal *internal;\n    void *opaque;\n    int bit_rate;\n    int bit_rate_tolerance;\n    int global_quality;\n    int compression_level;\n    int flags;\n    int flags2;\n    uint8_t *extradata;\n    int extradata_size;\n    struct AVRational time_base;\n    int ticks_per_frame;\n    int delay;\n    int width;\n    int height;\n    int coded_width;\n    int coded_height;\n    int gop_size;\n    enum AVPixelFormat pix_fmt;\n    int me_method;\n    void (*draw_horiz_band)(struct AVCodecContext *, struct AVFrame *, int *, int, int, int);\n    AVPixelFormat (*get_format)(struct AVCodecContext *, enum AVPixelFormat *);\n    int max_b_frames;\n    float b_quant_factor;\n    int rc_strategy;\n    int b_frame_strategy;\n    float b_quant_offset;\n    int has_b_frames;\n    int mpeg_quant;\n    float i_quant_factor;\n    float i_quant_offset;\n    float lumi_masking;\n    float temporal_cplx_masking;\n    float spatial_cplx_masking;\n    float p_masking;\n    float dark_masking;\n    int slice_count;\n    int prediction_method;\n    int *slice_offset;\n    struct AVRational sample_aspect_ratio;\n    int me_cmp;\n    int me_sub_cmp;\n    int mb_cmp;\n    int ildct_cmp;\n    int dia_size;\n    int last_predictor_count;\n    int pre_me;\n    int me_pre_cmp;\n    int pre_dia_size;\n    int me_subpel_quality;\n    int dtg_active_format;\n    int me_range;\n    int intra_quant_bias;\n    int inter_quant_bias;\n    int slice_flags;\n    int xvmc_acceleration;\n    int mb_decision;\n    uint16_t *intra_matrix;\n    uint16_t *inter_matrix;\n    int scenechange_threshold;\n    int noise_reduction;\n    int me_threshold;\n    int mb_threshold;\n    int intra_dc_precision;\n    int skip_top;\n    int skip_bottom;\n    float border_masking;\n    int mb_lmin;\n    int mb_lmax;\n    int me_penalty_compensation;\n    int bidir_refine;\n    int brd_scale;\n    int keyint_min;\n    int refs;\n    int chromaoffset;\n    int scenechange_factor;\n    int mv0_threshold;\n    int b_sensitivity;\n    enum AVColorPrimaries color_primaries;\n    enum AVColorTransferCharacteristic color_trc;\n    enum AVColorSpace colorspace;\n    enum AVColorRange color_range;\n    enum AVChromaLocation chroma_sample_location;\n    int slices;\n    enum AVFieldOrder field_order;\n    int sample_rate;\n    int channels;\n    enum AVSampleFormat sample_fmt;\n    int frame_size;\n    int frame_number;\n    int block_align;\n    int cutoff;\n    int request_channels;\n    undefined field102_0x1bc;\n    undefined field103_0x1bd;\n    undefined field104_0x1be;\n    undefined field105_0x1bf;\n    uint64_t channel_layout;\n    uint64_t request_channel_layout;\n    enum AVAudioServiceType audio_service_type;\n    enum AVSampleFormat request_sample_fmt;\n    int (*get_buffer)(struct AVCodecContext *, struct AVFrame *);\n    void (*release_buffer)(struct AVCodecContext *, struct AVFrame *);\n    int (*reget_buffer)(struct AVCodecContext *, struct AVFrame *);\n    int (*get_buffer2)(struct AVCodecContext *, struct AVFrame *, int);\n    int refcounted_frames;\n    float qcompress;\n    float qblur;\n    int qmin;\n    int qmax;\n    int max_qdiff;\n    float rc_qsquish;\n    float rc_qmod_amp;\n    int rc_qmod_freq;\n    int rc_buffer_size;\n    int rc_override_count;\n    struct RcOverride *rc_override;\n    char *rc_eq;\n    int rc_max_rate;\n    int rc_min_rate;\n    float rc_buffer_aggressivity;\n    float rc_initial_cplx;\n    float rc_max_available_vbv_use;\n    float rc_min_vbv_overflow_use;\n    int rc_initial_buffer_occupancy;\n    int coder_type;\n    int context_model;\n    int lmin;\n    int lmax;\n    int frame_skip_threshold;\n    int frame_skip_factor;\n    int frame_skip_exp;\n    int frame_skip_cmp;\n    int trellis;\n    int min_prediction_order;\n    int max_prediction_order;\n    undefined field145_0x264;\n    undefined field146_0x265;\n    undefined field147_0x266;\n    undefined field148_0x267;\n    int64_t timecode_frame_start;\n    void (*rtp_callback)(struct AVCodecContext *, void *, int, int);\n    int rtp_payload_size;\n    int mv_bits;\n    int header_bits;\n    int i_tex_bits;\n    int p_tex_bits;\n    int i_count;\n    int p_count;\n    int skip_count;\n    int misc_bits;\n    int frame_bits;\n    char *stats_out;\n    char *stats_in;\n    int workaround_bugs;\n    int strict_std_compliance;\n    int error_concealment;\n    int debug;\n    int debug_mv;\n    int err_recognition;\n    undefined field169_0x2bc;\n    undefined field170_0x2bd;\n    undefined field171_0x2be;\n    undefined field172_0x2bf;\n    int64_t reordered_opaque;\n    struct AVHWAccel *hwaccel;\n    void *hwaccel_context;\n    uint64_t error[8];\n    int dct_algo;\n    int idct_algo;\n    int bits_per_coded_sample;\n    int bits_per_raw_sample;\n    int lowres;\n    struct AVFrame *coded_frame;\n    int thread_count;\n    int thread_type;\n    int active_thread_type;\n    int thread_safe_callbacks;\n    int (*execute)(struct AVCodecContext *, int (*)(struct AVCodecContext *, void *), void *, int *, int, int);\n    int (*execute2)(struct AVCodecContext *, int (*)(struct AVCodecContext *, void *, int, int), void *, int *, int);\n    void *thread_opaque;\n    int nsse_weight;\n    int profile;\n    int level;\n    enum AVDiscard skip_loop_filter;\n    enum AVDiscard skip_idct;\n    enum AVDiscard skip_frame;\n    uint8_t *subtitle_header;\n    int subtitle_header_size;\n    int error_rate;\n    struct AVPacket *pkt;\n    undefined field200_0x36c;\n    undefined field201_0x36d;\n    undefined field202_0x36e;\n    undefined field203_0x36f;\n    uint64_t vbv_delay;\n    int side_data_only_packets;\n    int initial_padding;\n    struct AVRational framerate;\n    struct AVRational pkt_timebase;\n    struct AVCodecDescriptor *codec_descriptor;\n    undefined field210_0x394;\n    undefined field211_0x395;\n    undefined field212_0x396;\n    undefined field213_0x397;\n    int64_t pts_correction_num_faulty_pts;\n    int64_t pts_correction_num_faulty_dts;\n    int64_t pts_correction_last_pts;\n    int64_t pts_correction_last_dts;\n    char *sub_charenc;\n    int sub_charenc_mode;\n    int skip_alpha;\n    int seek_preroll;\n    uint16_t *chroma_intra_matrix;\n    uint8_t *dump_separator;\n    char *codec_whitelist;\n    int bit_depth_luma;\n    int chroma_format_idc;\n    undefined field227_0x3dc;\n    undefined field228_0x3dd;\n    undefined field229_0x3de;\n    undefined field230_0x3df;\n};\n\nstruct _DUSS_MSG_SATURATION_t\n{\n    int8_t Value;\n};\n\nstruct _DUSS_MSG_ZOOM_P_t\n{\n    uint16_t Ratio;\n};\n\nstruct _DUSS_MSG_ZOOM_S_t\n{\n    uint16_t Step : 8;\n    uint16_t Direction : 1;\n    uint16_t Reserved : 7;\n};\n\nstruct _DUSS_MSG_ZOOM_C_t\n{\n    uint16_t Speed : 8;\n    uint16_t Direction : 1;\n    uint16_t Reserved : 7;\n};\n\nunion anon_union_conflict21a2_for_OZoomRaUnion\n{\n    DUSS_MSG_ZOOM_S_t ZoomS;\n    DUSS_MSG_ZOOM_P_t ZoomP;\n    DUSS_MSG_ZOOM_C_t ZoomC;\n};\n\nstruct _DUSS_MSG_ZOOM_CTRL_MODE_t_\n{\n    uint8_t DZoomMode : 2;\n    uint8_t Reserved1 : 1;\n    uint8_t DZoomCtrl : 1;\n    uint8_t OZoomMode : 2;\n    uint8_t Reserved2 : 1;\n    uint8_t OZoomCtrl : 1;\n};\n\nstruct _DUSS_MSG_ZOOM_PARAM_t\n{\n    DUSS_MSG_ZOOM_CTRL_MODE_t Mode;\n    union anon_union_conflict21a2_for_OZoomRaUnion OZoomRaUnion;\n    union anon_union_conflict21a2_for_OZoomRaUnion DZoomRaUnion;\n};\n\nstruct _DUSS_MSG_CAPTURE_MODE_t\n{\n    uint8_t Op;\n    uint8_t Mcap_Cnt;\n    uint8_t Ccap_Type;\n    uint8_t Ccap_Cnt;\n    uint16_t Ccap_Interval;\n};\n\nstruct _DUSS_MSG_ISO_t\n{\n    uint8_t Value;\n};\n\nstruct _DUSS_MSG_SD_STATUS_t\n{\n    uint8_t Inserted : 1;\n    uint8_t Status : 4;\n    uint8_t Reserved : 3;\n};\n\nstruct _DUSS_MSG_SD_INFO_t\n{\n    DUSS_MSG_SD_STATUS_t SdStatus;\n    uint32_t Capacity;\n    uint32_t SpareCap;\n    uint32_t RemainShots;\n    uint32_t RemainTime;\n};\n\nstruct _DUSS_MSG_EXPO_MODE_t\n{\n    uint8_t Mode;\n    uint8_t ScnMode;\n};\n\nstruct _DUSS_MSG_FLIP\n{\n    uint8_t type;\n    uint8_t angle;\n};\n\nstruct _DUSS_MSG_SHARPNESS_t\n{\n    int8_t Value;\n};\n\nstruct _DUSS_MSG_CONTICAP_PARAM_t\n{\n    uint8_t Type;\n    uint8_t Cnt;\n    uint16_t Interval;\n};\n\nstruct _DUSS_MSG_P_STORAGE_FMT_t\n{\n    uint8_t Fmt;\n};\n\nstruct _DUSS_MSG_RECORD_t\n{\n    uint8_t Op : 2;\n    uint8_t type : 4;\n    uint8_t Reserved : 2;\n};\n\nstruct _DUSS_MSG_WB_t\n{\n    uint8_t Mode;\n    uint8_t WbCt;\n};\n\nstruct _DUSS_MSG_CONTRAST_t\n{\n    int8_t Value;\n};\n\nstruct _DUSS_MSG_SHUTTER_A_t\n{\n    uint16_t Value : 15;\n    uint16_t Flag : 1;\n    uint8_t Decimal;\n};\n\nstruct _DUSS_MSG_SHUTTER_SPEED_t\n{\n    uint8_t Mode;\n    DUSS_MSG_SHUTTER_A_t ShutterA;\n};\n\nstruct _DUSS_MSG_DIGITAL_EFFECT_t\n{\n    uint8_t Effect;\n};\n\nstruct _DUSS_MSG_EV_BIAS_t\n{\n    uint8_t Bias;\n};\n\nstruct _DUSS_MSG_V_STORAGE_FMT_t\n{\n    uint8_t Fmt;\n};\n\nstruct _DUSS_MSG_SINGLE_PLAY_CRTL_t\n{\n    uint32_t FileIndex;\n};\n\nstruct _DUSS_MSG_SCENE_MODE_t\n{\n    uint8_t Mode;\n};\n\nstruct _DUSS_MSG_SET_FOCUS_REGION_t\n{\n    float x;\n    float y;\n};\n\nstruct _DUSS_MSG_V_SS_CONFIG_t\n{\n    uint8_t Op : 1;\n    uint8_t Reserved : 7;\n};\n\nstruct _DUSS_MSG_V_FORMAT_t\n{\n    uint8_t FSResolution;\n    uint8_t FrameRate;\n    uint8_t Fov;\n    DUSS_MSG_V_SS_CONFIG_t SSConfig;\n    uint8_t SSResolution;\n};\n\nstruct _DUSS_MSG_P_SIZE_t\n{\n    uint8_t Size;\n    uint8_t AspectRatio;\n};\n\nstruct _DUSS_MSG_CAPTURE_t\n{\n    uint8_t Op;\n};\n\nunion anon_union_conflict2c9b_for_field_3\n{\n    DUSS_MSG_APERTURE_t aperture;\n    DUSS_MSG_SHUTTER_SPEED_t shut_spd;\n    DUSS_MSG_ISO_t iso;\n    DUSS_MSG_EXPO_MODE_t expo_mode;\n    DUSS_MSG_EV_BIAS_t ev_bias;\n    DUSS_MSG_P_STORAGE_FMT_t p_storage;\n    DUSS_MSG_WB_t wb;\n    DUSS_MSG_SCENE_MODE_t scene_mode;\n    DUSS_MSG_DIGITAL_EFFECT_t digit_effect;\n    DUSS_MSG_P_SIZE_t p_size;\n    DUSS_MSG_SET_FOCUS_REGION_t focus;\n    DUSS_MSG_CAPTURE_MODE_t cap_mode;\n    DUSS_MSG_CONTICAP_PARAM_t conticap;\n    DUSS_MSG_V_STORAGE_FMT_t v_storage;\n    DUSS_MSG_V_FORMAT_t v_fmt;\n    DUSS_MSG_VIDEO_STANDARD_t v_std;\n    DUSS_MSG_WORKMODE_t work_mode;\n    DUSS_MSG_SINGLE_PLAY_CRTL_t play_ctrl_data;\n    DUSS_MSG_V_CRTL_t video_ctrl_data;\n    DUSS_MSG_SD_INFO_t sd_info;\n    DUSS_MSG_ZOOM_PARAM_t zoom_param;\n    DUSS_MSG_FLIP_t flip_mode;\n    DUSS_MSG_SHARPNESS_t sharpness;\n    DUSS_MSG_CONTRAST_t contrast;\n    DUSS_MSG_SATURATION_t saturation;\n    uint8_t lv_format;\n    DUSS_MSG_CAPTURE_t cap_photo;\n    DUSS_MSG_RECORD_t cap_video;\n    UAV_RECORD_MODE_t rec_mode;\n    uint8_t uav_low_power_mode;\n    uint8_t uav_power_policy;\n    uint8_t HDVT_pref;\n    uint8_t enc_roi;\n    uint8_t white_balance;\n};\n\nstruct __gs_camera_cmd\n{\n    uint16_t cmd_id;\n    uint16_t session_id;\n    uint8_t ack_result;\n    union anon_union_conflict2c9b_for_field_3 field_3;\n    undefined field4_0x16;\n    undefined field5_0x17;\n    void (*call_back_handler)(struct __gs_camera_cmd *);\n};\n\nstruct bridge_io_pkt\n{\n    uint32_t paddr;\n    uint32_t size;\n    uint8_t notify;\n    undefined field3_0x9;\n    undefined field4_0xa;\n    undefined field5_0xb;\n};\n\nstruct gs_lv_pkt\n{\n    void *align_vaddr;\n    int align_size;\n    void *vaddr;\n    void *payload_vaddr;\n    uint32_t payload_size;\n    bridge_io_pkt_t pkt;\n};\n\nstruct uav_gimbal_ops\n{\n    int (*ctrl_start)(void *);\n    int (*ctrl_racing_start)(void *);\n    int (*ctrl_stop)(void *);\n    int (*reset_init_value)(void *);\n    int (*turndown_start)(void *);\n    int (*turndown_stop)(void *);\n    int (*set_workmode)(void *, uint8_t);\n    int (*ctrl_set_racing_pitch_offset)(void *, int16_t);\n    int (*ctrl_set_racing_yaw_offset)(void *, int16_t);\n    int (*ctrl_set_racing_pitch_sense)(void *, float);\n    int (*ctrl_set_racing_yaw_sense)(void *, float);\n    int (*ctrl_get_state)(void *, uint8_t *);\n};\n\nstruct duss_hal_icc_config_t\n{\n    enum duss_hal_icc_role_t role;\n    enum duss_hal_icc_shm_type_t shm_type;\n    uint32_t shm_size;\n};\n\nstruct RcOverride\n{\n    int start_frame;\n    int end_frame;\n    int qscale;\n    float quality_factor;\n};\n\nstruct uav_camera_ops\n{\n    int (*send_camera_cmd)(void *, gs_camera_cmd_t *, _Bool);\n    int (*set_camera_param)(void *, uint8_t, uint8_t);\n    int (*get_rec_time)(void *, int *, int *);\n};\n\nstruct duss_event_cmd_desc\n{\n    duss_result_t (*req_cb)(struct duss_event_client *, duss_event_t *, void *);\n    duss_result_t (*ack_cb)(struct duss_event_client *, duss_event_t *, void *);\n    uint32_t flags;\n};\n\nstruct cJSON\n{\n    struct cJSON *next;\n    struct cJSON *prev;\n    struct cJSON *child;\n    int type;\n    char *valuestring;\n    int valueint;\n    double valuedouble;\n    char *string;\n    undefined field8_0x24;\n    undefined field9_0x25;\n    undefined field10_0x26;\n    undefined field11_0x27;\n};\n\nstruct vr_device_ops\n{\n    int (*get_video_scale)(void *, float *);\n    int (*set_video_scale)(void *, float);\n    int (*adjust_fov)(void *, uint32_t, int16_t, int16_t);\n    int (*set_brightness_value)(void *, uint8_t);\n    int (*get_brightness_value)(void *, uint8_t *);\n    int (*get_device_volume)(void *, uint8_t, uint32_t *);\n    int (*set_device_volume)(void *, uint8_t, uint32_t);\n    int (*calibrate_imu)(char *);\n    int (*initialize_imu)(void *, ...);\n    int (*destroy_imu)(void *, ...);\n    int (*start_imu)(void *, ...);\n    int (*stop_imu)(void *, ...);\n    int (*get_imu_init_status)(void *, ...);\n    int (*get_imu_attitude)(float *);\n    int (*format_sd)(void *);\n    int (*get_sd_info)(void *, gs_local_sd_info_t *);\n    int (*get_battery_info)(void *, DUSS_MSG_RC_BAT_INFO_t *);\n    int (*get_device_active_state)(void *, int);\n    int (*get_upgrade_info)(void *, _Bool *, uint8_t *, uint8_t *);\n    int (*av_in_us_get_brightness)(void *, uint8_t *);\n    int (*av_in_us_set_brightness)(void *, uint8_t);\n    int (*av_in_us_get_saturation)(void *, uint8_t *);\n    int (*av_in_us_set_saturation)(void *, uint8_t);\n    int (*reset_user_settings)(void *);\n    int (*event_tracking)(void *, int32_t, uint8_t, uint8_t);\n    void (*get_flight_stat)(uint8_t *);\n    void (*set_flight_stat)(uint8_t);\n    int (*watermarker_us_get_flag)(void *, uint8_t *);\n    int (*watermarker_us_set_flag)(void *, uint8_t);\n    int (*watermarker_us_reset)(void *);\n};\n\nstruct pl_decoder_itf_t\n{\n    int (*create)(pl_decoder_handle_t *, void *, void *);\n    int (*destroy)(pl_decoder_handle_t);\n    int (*init)(pl_decoder_handle_t);\n    int (*config)(pl_decoder_handle_t, void *);\n    int (*prepare)(pl_decoder_handle_t);\n    int (*release)(pl_decoder_handle_t);\n    int (*decode)(pl_decoder_handle_t, void *, uint32_t, void *, uint32_t *);\n    int (*push_frame)(pl_decoder_handle_t, void *, uint32_t);\n    int (*set_input_datacb)(pl_decoder_handle_t, void *, input_data_cb);\n    int (*set_output_datacb)(pl_decoder_handle_t, void *, output_data_cb);\n};\n\nstruct duss_event_ack_identify\n{\n    duss_msg_id_t msg_id;\n    uint16_t peer_host;\n    uint16_t seq_id;\n    uint16_t valid;\n    undefined field4_0xa;\n    undefined field5_0xb;\n    void *buffer_ptr;\n    uint32_t buffer_size;\n};\n\nstruct vcm_ops\n{\n    int (*register_channel_switch_callback)(void *, gs_video_channel_switch_callback_t, void *);\n    int (*register_channel_push_callback)(void *, gs_video_channel_push_callback_t, void *);\n    int (*query_current_channel)(void *, gs_video_channel_id_t *);\n    int (*enable_channel)(void *, gs_main_channel_id_t);\n    int (*disable_channel)(void *, gs_main_channel_id_t);\n    int (*enable_sub_channel)(void *, gs_video_channel_id_t *);\n    int (*disable_sub_channel)(void *, gs_video_channel_id_t *);\n    int (*local_playback_on)(void *, char *);\n    int (*local_playback_off)(void *);\n    int (*rc_setting_on)(void *);\n    int (*rc_setting_off)(void *);\n};\n\nstruct AVOptionRange\n{\n    char *str;\n    undefined field1_0x4;\n    undefined field2_0x5;\n    undefined field3_0x6;\n    undefined field4_0x7;\n    double value_min;\n    double value_max;\n    double component_min;\n    double component_max;\n    int is_range;\n    undefined field10_0x2c;\n    undefined field11_0x2d;\n    undefined field12_0x2e;\n    undefined field13_0x2f;\n};\n\nstruct debug_osd_item\n{\n    char *name;\n    int value;\n};\n\nstruct debug_cam_osd_info\n{\n    debug_osd_item_t items[26];\n    int n_items;\n};\n\nstruct AVPanScan\n{\n    int id;\n    int width;\n    int height;\n    int16_t position[3][2];\n};\n\nstruct AVProfile\n{\n    int profile;\n    char *name;\n};\n\nstruct loc_channel_addr\n{\n    uint16_t direct_host;\n};\n\nstruct __gs_common_cmd\n{\n    duss_msg_id_t msg_id;\n    uint16_t receiver_id;\n    uint16_t session_id;\n    uint8_t ack_result;\n    undefined field4_0x9;\n    undefined field5_0xa;\n    undefined field6_0xb;\n    uint8_t *msg_buf;\n    uint32_t msg_len;\n    uint8_t *resp_msg_buf;\n    uint32_t resp_msg_len;\n    void (*call_back_handler)(struct __gs_common_cmd *);\n};\n\nstruct duss_hal_mem_config_t\n{\n    uint32_t reserved;\n};\n\nstruct wl_channel_addr\n{\n    char *ifx;\n    uint16_t loc_port;\n    undefined field2_0x6;\n    undefined field3_0x7;\n    char *address;\n    uint16_t rmt_port;\n    uint16_t mtu;\n    _Bool is_server;\n    duss_wl_prot_type_t protocol;\n    duss_wl_prio_t priority;\n    undefined field10_0x13;\n    int flags;\n};\n\nstruct AVDeviceCapabilitiesQuery\n{\n};\n\nstruct __sbuf\n{\n    uchar *_base;\n    int _size;\n};\n\nstruct vdec_video_file_info\n{\n    uint32_t strm_type;\n    uint32_t total_time;\n    uint32_t frame_interval;\n    uint32_t fps_numerator;\n    uint32_t fps_denorm;\n    uint32_t width;\n    uint32_t height;\n    int32_t rotation;\n    enum DUSS_FILE_FORMAT video_format;\n};\n\nstruct fd_set\n{\n    ulong fds_bits[32];\n};\n\nstruct duss_mb_client\n{\n    uint16_t this_host;\n    undefined field1_0x2;\n    undefined field2_0x3;\n    duss_result_t (*msg_cb)(struct duss_mb_client *, uint16_t, uint16_t, duss_msg_attrib_t, duss_msg_id_t, uint8_t *, int32_t, void *);\n    void *msg_cb_userdata;\n    duss_result_t (*filter_cb)(struct duss_mb_client *, struct duss_mb_route_item_t *, uint16_t, uint16_t, uint16_t, duss_msg_attrib_t, duss_msg_id_t, uint8_t *, int32_t, void *);\n    void *filter_cb_userdata;\n    struct duss_mb_route_table_t *route_table;\n    struct duss_osal_mutex_handle_t *lock;\n    struct duss_osal_task_handle_t *task;\n    int fd_max;\n    struct fd_set fds;\n    _Bool finish;\n    undefined field13_0xa5;\n    undefined field14_0xa6;\n    undefined field15_0xa7;\n    int channel_num;\n    void *channel[32];\n    int target_num;\n    void *target[64];\n};\n\nstruct AVPacket\n{\n    struct AVBufferRef *buf;\n    undefined field1_0x4;\n    undefined field2_0x5;\n    undefined field3_0x6;\n    undefined field4_0x7;\n    int64_t pts;\n    int64_t dts;\n    uint8_t *data;\n    int size;\n    int stream_index;\n    int flags;\n    struct AVPacketSideData *side_data;\n    int side_data_elems;\n    int duration;\n    void (*destruct)(struct AVPacket *);\n    void *priv;\n    undefined field16_0x3c;\n    undefined field17_0x3d;\n    undefined field18_0x3e;\n    undefined field19_0x3f;\n    int64_t pos;\n    int64_t convergence_duration;\n    _Bool is_last_section;\n    undefined field23_0x51;\n    undefined field24_0x52;\n    undefined field25_0x53;\n    int32_t nal_total_size;\n};\n\nstruct icc_channel_addr\n{\n    char *obj_name;\n    char *snd_chn;\n    char *rcv_chn;\n    struct duss_hal_icc_config_t cfg;\n};\n\nstruct duss_mb_route_table_t\n{\n    void *priv_data;\n    int target_size;\n    struct duss_mb_route_item_t *targets[128];\n};\n\nstruct AVCodecInternal\n{\n};\n\nstruct pl_decoder_t\n{\n    struct pl_decoder_itf_t *itf;\n    void *context;\n    struct AVRational time_base;\n    void *me;\n    _Bool running;\n    undefined field5_0x15;\n    undefined field6_0x16;\n    undefined field7_0x17;\n    struct duss_osal_msgq_handle_t *packet_queue;\n    struct duss_osal_msgq_handle_t *frame_queue;\n    struct duss_osal_task_handle_t *decode_task;\n};\n\nstruct gs_local_panorama_info\n{\n    char file_path[256];\n    uint8_t file_type;\n    undefined field2_0x101;\n    undefined field3_0x102;\n    undefined field4_0x103;\n    int gfxLayerFd;\n};\n\nstruct duss_hal_i2c_config_t\n{\n    int baudrate;\n    duss_i2c_addr_len_t addr_len;\n    undefined field2_0x5;\n    undefined field3_0x6;\n    undefined field4_0x7;\n};\n\nstruct duss_hal_storage_params\n{\n    enum duss_storage_client_type_t client_id;\n    void *user_data;\n    void (*callback)(struct duss_hal_storage_info *, void *);\n};\n\nstruct AVBufferRef\n{\n    struct AVBuffer *buffer;\n    uint8_t *data;\n    int size;\n};\n\nstruct sem_t\n{\n    uint count;\n};\n\nstruct duss_osal_mutex_handle_t\n{\n    struct duss_osal_mutex_attrib_t attrib;\n    struct sem_t sema;\n};\n\nstruct stick_mode_self_define_t\n{\n    uint8_t ch_reverse : 1;\n    uint8_t stick_channel : 7;\n};\n\nstruct rc_set_stick_mode_pack_t\n{\n    uint8_t stick_mode;\n    struct stick_mode_self_define_t ch_setup[0];\n};\n\nstruct duss_osal_task_attrib_t\n{\n    char *name;\n    duss_osal_task_entry_t entry;\n    void *param;\n    enum duss_osal_priority_t priority;\n    uint32_t stack_size;\n    uint32_t time_slice;\n    _Bool detached;\n    undefined field7_0x19;\n    undefined field8_0x1a;\n    undefined field9_0x1b;\n    uint32_t affinity_mask;\n};\n\nstruct SwrContext\n{\n};\n\nstruct AVHWAccel\n{\n    char *name;\n    enum AVMediaType type;\n    enum AVCodecID id;\n    enum AVPixelFormat pix_fmt;\n    int capabilities;\n    struct AVHWAccel *next;\n    int (*alloc_frame)(struct AVCodecContext *, struct AVFrame *);\n    int (*start_frame)(struct AVCodecContext *, uint8_t *, uint32_t);\n    int (*decode_slice)(struct AVCodecContext *, uint8_t *, uint32_t);\n    int (*end_frame)(struct AVCodecContext *);\n    int frame_priv_data_size;\n    void (*decode_mb)(struct MpegEncContext *);\n    int (*init)(struct AVCodecContext *);\n    int (*uninit)(struct AVCodecContext *);\n    int priv_data_size;\n};\n\nstruct netlink_channel_addr\n{\n    uint16_t host_pid;\n    uint16_t group;\n    uint16_t target_pid;\n};\n\nstruct duss_hal_uart_config_t\n{\n    int baudrate;\n    duss_uart_parity_t parity;\n    duss_uart_stopbit_t stopbit;\n    duss_uart_wordlen_t wordlen;\n    undefined field4_0x7;\n};\n\nstruct uart_channel_addr\n{\n    char *obj_name;\n    struct duss_hal_uart_config_t cfg;\n};\n\nstruct usb_channel_addr\n{\n    char *device;\n    uint32_t vid;\n    uint32_t pid;\n    uint16_t interface;\n    uint16_t altsetting;\n    uint8_t in_ep;\n    uint8_t out_ep;\n    undefined field7_0x12;\n    undefined field8_0x13;\n};\n\nstruct duss_hal_spi_config_t\n{\n    int mode;\n    int baudrate;\n    int lsb_first;\n    int bits_per_word;\n};\n\nstruct spi_channel_addr\n{\n    char *obj_name;\n    struct duss_hal_spi_config_t cfg;\n};\n\nstruct duss_hal_can_config_t\n{\n    duss_mb_can_chip_t chip;\n    undefined field1_0x1;\n    undefined field2_0x2;\n    undefined field3_0x3;\n    uint32_t baudrate;\n    duss_mb_host_id_t target;\n    undefined field6_0xa;\n    undefined field7_0xb;\n    uint32_t rx;\n    uint32_t tx;\n};\n\nstruct can_channel_addr\n{\n    char *obj_name;\n    struct duss_hal_can_config_t cfg;\n};\n\nstruct duss_hal_bulk_config_t\n{\n};\n\nstruct bulk_channel_addr\n{\n    char *obj_name;\n    struct duss_hal_bulk_config_t cfg;\n};\n\nstruct i2c_channel_addr\n{\n    char *obj_name;\n    struct duss_hal_i2c_config_t cfg;\n};\n\nunion anon_union_conflict1232_for_channel\n{\n    struct loc_channel_addr local;\n    struct ip_channel_addr ip;\n    struct wl_channel_addr wl;\n    struct uart_channel_addr uart;\n    struct can_channel_addr can;\n    struct i2c_channel_addr i2c;\n    struct spi_channel_addr spi;\n    struct hpi_channel_addr hpi;\n    struct usb_channel_addr usb;\n    struct usbacc_channel_addr usbacc;\n    struct icc_channel_addr icc;\n    struct netlink_channel_addr netlink;\n    struct bulk_channel_addr bulk;\n};\n\nstruct AVPicture\n{\n    uint8_t *data[8];\n    int linesize[8];\n};\n\nstruct debug_cp_osd_info\n{\n    debug_osd_item_t items[4];\n    int n_items;\n};\n\nstruct AVDictionary\n{\n};\n\nstruct duss_hal_mem_param_t\n{\n    uint32_t reserved;\n};\n\nstruct ion_info\n{\n    duss_hal_obj_handle_t mem_obj;\n    struct duss_hal_mem_config_t mem_cfg;\n    struct duss_hal_mem_param_t mem_param;\n    duss_hal_mem_handle_t ion_buf;\n    void *ion_virt_addr;\n    uint32_t ion_len;\n    struct duss_osal_mutex_handle_t *ion_mutex;\n    uint32_t start_addr;\n    uint32_t stop_addr;\n    uint32_t size_waste;\n};\n\nstruct audio_resampler_t\n{\n    struct SwrContext *swr_ctx;\n    int src_rate;\n    int dst_rate;\n    int src_nb_channels;\n    int dst_nb_channels;\n    enum AVSampleFormat src_sample_fmt;\n    enum AVSampleFormat dst_sample_fmt;\n};\n\nstruct pcm\n{\n    int fd;\n    uint flags;\n    uint running : 1;\n    uint prepared : 1;\n    undefined field4_0x9;\n    undefined field5_0xa;\n    undefined field6_0xb;\n    int underruns;\n    uint buffer_size;\n    uint boundary;\n    char error[128];\n    struct pcm_config config;\n    struct snd_pcm_mmap_status *mmap_status;\n    struct snd_pcm_mmap_control *mmap_control;\n    struct snd_pcm_sync_ptr *sync_ptr;\n    void *mmap_buffer;\n    uint noirq_frames_per_msec;\n    int wait_for_avail_min;\n    struct timespec tstamp;\n    int (*ev_callback)(int, void *);\n    void *ev_params;\n    char ev_running;\n    undefined field22_0xed;\n    undefined field23_0xee;\n    undefined field24_0xef;\n    pthread_t ev_thread;\n    char chg_3le_to_le;\n    undefined field27_0xf5;\n    undefined field28_0xf6;\n    undefined field29_0xf7;\n    char *buffer_4le;\n    uint size_4le;\n};\n\nstruct duss_mb_filter_t\n{\n    uint16_t source;\n    uint16_t target;\n    duss_msg_id_t msg_id;\n    duss_msg_attrib_t attrib;\n    undefined field4_0xa;\n    undefined field5_0xb;\n};\n\nstruct duss_hal_obj_dev_t\n{\n    char *name;\n    duss_hal_state_t obj_state;\n    duss_hal_class_t obj_class;\n    uint16_t obj_index;\n    int32_t obj_refcnt;\n    struct duss_osal_mutex_handle_t *obj_lock;\n    struct duss_osal_mutex_handle_t *app_lock;\n    duss_result_t (*open)(duss_hal_obj_handle_t, void *);\n    duss_result_t (*close)(duss_hal_obj_handle_t);\n    duss_result_t (*set_cfg)(duss_hal_obj_handle_t, void *);\n    duss_result_t (*get_cfg)(duss_hal_obj_handle_t, void *);\n    duss_result_t (*start)(duss_hal_obj_handle_t, void *);\n    duss_result_t (*stop)(duss_hal_obj_handle_t, void *);\n};\n\nstruct duss_hal_obj\n{\n    struct duss_hal_obj_dev_t dev;\n    void *dev_class;\n    void *dev_priv;\n};\n\nstruct duss_event\n{\n    duss_msg_id_t msg_id;\n    uint16_t peer_host;\n    duss_msg_attrib_t attrib;\n    uint16_t seq_id;\n    undefined field4_0xa;\n    undefined field5_0xb;\n    uint32_t msg_len;\n    uint8_t msg_buf[0];\n};\n\nstruct common_cmd_ops\n{\n    int (*send_common_cmd)(void *, gs_common_cmd_t *, _Bool);\n};\n\nstruct local_playback_ops\n{\n    int (*open)(void *, char *);\n    int (*close)(void *);\n    int (*stop)(void *);\n    int (*pause)(void *);\n    int (*resume)(void *);\n    int (*seek)(void *, uint32_t);\n    int (*seek_time)(void *, uint32_t);\n    int (*get_duration)(char *, uint32_t *);\n    int (*get_cur_time)(void *, uint32_t *, uint32_t *);\n    int (*get_state)(void *, uint32_t *);\n    int (*get_file_info)(char *, vdec_video_file_info_t *);\n    int (*is_playing)(void *, _Bool *);\n    int (*delete_file)(char *);\n};\n\nstruct rc_ops\n{\n    int (*get_version)(void *, char *, char *, int8_t *);\n    int (*get_version_async)(void *);\n    int (*set_subtrim)(void *, uint8_t, int16_t);\n    int (*set_subtrim_async)(void *, uint8_t, int16_t);\n    int (*get_subtrim)(void *, struct rc_set_all_st_t *, int8_t *);\n    int (*get_subtrim_async)(void *);\n    int (*set_reverse)(void *, uint8_t, uint8_t);\n    int (*get_reverse)(void *, union rc_set_reverse_t *, int8_t *);\n    int (*get_reverse_async)(void *);\n    int (*set_endpoint)(void *, uint8_t, uint8_t, uint8_t);\n    int (*set_endpoint_async)(void *, uint8_t, uint8_t, uint8_t);\n    int (*get_endpoint)(void *, struct rc_set_all_ep_t *, int8_t *);\n    int (*get_endpoint_async)(void *);\n    int (*set_func_mode)(void *, uint8_t, uint8_t, uint8_t);\n    int (*get_func_mode)(void *, struct rc_set_function_pack_t *);\n    int (*set_stick_mode)(void *, uint8_t);\n    int (*get_stick_mode)(void *, struct rc_set_stick_mode_pack_t *, int8_t *);\n    int (*get_stick_mode_ext)(void *, uint8_t *);\n    int (*get_stick_mode_async)(void *);\n    int (*set_stick_cali)(void *, uint8_t, uint8_t, uint8_t *);\n    int (*get_stick_cali)(void *, uint8_t, int8_t *, int8_t *, int8_t *, int8_t *);\n    int (*set_warning)(void *, struct rc_set_warning_mode_pack_t);\n    int (*get_warning)(void *, struct rc_set_warning_mode_pack_t *);\n    int (*monitor)(void *, struct rc_monitor_pack_t *);\n    int (*reset_all_get_status)(void *);\n    int (*reset_default_async)(void *, uint8_t);\n    uint8_t (*get_lock_state)(void *);\n    uint8_t (*get_link_state)(void *);\n};\n\nstruct __gs_gui_config\n{\n    gs_queue_t *osd_gen_data_queue;\n    gs_queue_t *osd_home_data_queue;\n    gs_queue_t *file_del_queue;\n    gs_queue_t *camera_stat_queue;\n    gs_queue_t *camera_cap_param_queue;\n    gs_queue_t *camera_pb_param_queue;\n    gs_queue_t *camera_file_info_queue;\n    gs_queue_t *camera_dcf_abstract_queue;\n    gs_queue_t *camera_fov_param_queue;\n    gs_queue_t *rc_bat_stat_queue;\n    gs_queue_t *signal_quality_queue;\n    gs_queue_t *uav_bat_common_info_queue;\n    gs_queue_t *sdr_wl_env_queue;\n    gs_queue_t *sdr_uav_data_queue;\n    gs_queue_t *sdr_gnd_data_queue;\n    gs_queue_t *rc_param_queue;\n    gs_queue_t *rc_pushed_gps_queue;\n    gs_queue_t *racing_drone_fc_osd_queue;\n    _Bool *touchpadLockState;\n    void *ctx;\n    ext_fc_ops_t ext_fc_ops;\n    vr_device_ops_t vdev_ops;\n    local_playback_ops_t local_pb_ops;\n    uav_camera_ops_t uav_cam_ops;\n    uav_gimbal_ops_t gimbal_ops;\n    modem_ops_t modem_ops;\n    rc_ops_t rc_ops;\n    vcm_ops_t vcm_ops;\n    common_cmd_ops_t cmn_cmd_ops;\n    int (*get_codec_debug_osd_info)(void *, debug_codec_osd_info_t *);\n    int (*get_cam_debug_osd_info)(void *, debug_cam_osd_info_t *);\n    int (*get_temp_debug_osd_info)(void *, debug_temp_osd_info_t *);\n    int (*get_uav_max_temp)(void *, int32_t *);\n    int (*get_cp_debug_osd_info)(void *, debug_cp_osd_info_t *);\n    int (*set_racing_enc_strategy)(void *, uint8_t);\n    int (*is_camera_in_record)(void *, int *);\n    int (*get_cparm_version)(void *, char *);\n    int (*get_dsp_version)(void *, char *);\n    int (*get_flight_time)(void *, uint64_t *);\n    int (*get_arm_status)(void *, uint8_t *);\n    int (*clear_adb_log_info)(void *);\n    int (*reset_camera_param)(void *);\n    int (*set_low_power_mode)(void *, _Bool);\n    int (*get_uav_version)(void *, char *, char *);\n    int (*get_uav_hardware_version)(void *, char *);\n    int (*get_uav_power_status)(void *, uint8_t *, uint8_t *);\n    int (*get_uav_camera_type)(void *, uint8_t *);\n    int (*get_liveview_fps)(void *, uint8_t *);\n    int (*fbdev_open)(void);\n    int (*fbdev_disp_frame)(int);\n    int (*fbdev_close)(void);\n    int (*record_liveview)(void *, _Bool);\n    int (*get_liveview_state)(void *, int *);\n    record_mode_t (*get_liveview_record_mode)(void *);\n    int (*set_liveview_record_mode)(void *, record_mode_t);\n    int (*get_liveview_record_enable)(void *);\n    int (*set_liveview_record_enable)(void *, _Bool, enum Record_sender);\n    int (*get_liveview_record_time)(void *, int *);\n    int (*enable_audio_liveview)(void *, _Bool);\n    int (*get_frame_delay_e2e)(void *, uint16_t *);\n    int (*get_chnl_status)(void *, uint16_t *);\n    int (*get_pigeon_battery_info)(void *, gs_battery_voltage_t *);\n    int (*get_sd_status)(void *, int, uint8_t *, uint32_t *, uint32_t *);\n    int (*play_pwm_buzzer)(void *, int, int, int);\n    int (*buzzer_enable_bat)(void *, _Bool);\n};\n\nstruct AVCodecParserContext\n{\n    void *priv_data;\n    struct AVCodecParser *parser;\n    int64_t frame_offset;\n    int64_t cur_offset;\n    int64_t next_frame_offset;\n    int pict_type;\n    int repeat_pict;\n    int64_t pts;\n    int64_t dts;\n    int64_t last_pts;\n    int64_t last_dts;\n    int fetch_timestamp;\n    int cur_frame_start_index;\n    int64_t cur_frame_offset[4];\n    int64_t cur_frame_pts[4];\n    int64_t cur_frame_dts[4];\n    int flags;\n    undefined field17_0xb4;\n    undefined field18_0xb5;\n    undefined field19_0xb6;\n    undefined field20_0xb7;\n    int64_t offset;\n    int64_t cur_frame_end[4];\n    int key_frame;\n    undefined field24_0xe4;\n    undefined field25_0xe5;\n    undefined field26_0xe6;\n    undefined field27_0xe7;\n    int64_t convergence_duration;\n    int dts_sync_point;\n    int dts_ref_dts_delta;\n    int pts_dts_delta;\n    undefined field32_0xfc;\n    undefined field33_0xfd;\n    undefined field34_0xfe;\n    undefined field35_0xff;\n    int64_t cur_frame_pos[4];\n    int64_t pos;\n    int64_t last_pos;\n    int duration;\n    enum AVFieldOrder field_order;\n    enum AVPictureStructure picture_structure;\n    int output_picture_number;\n};\n\nstruct duss_osal_msgq_attrib_t\n{\n    char *name;\n    uint32_t buf_size;\n};\n\nstruct duss_osal_msgq_handle_t\n{\n    struct duss_osal_msgq_attrib_t attrib;\n    struct pthread_mutex_t r_mutex;\n    struct pthread_mutex_t s_mutex;\n    struct pthread_cond_t r_cond;\n    struct pthread_cond_t s_cond;\n    uint32_t buf_size;\n    uint32_t head;\n    uint32_t tail;\n    uint8_t *buffer;\n};\n\nstruct duss_mb_route_item_t\n{\n    uint8_t status;\n    char name[16];\n    undefined field2_0x11;\n    uint16_t target;\n    duss_mb_channel_t access_channel;\n    uint8_t distance;\n    duss_mb_pack_ver_t pack_ver;\n    undefined field7_0x17;\n    union anon_union_conflict1232_for_channel channel;\n    int filter_size;\n    struct duss_mb_filter_t *filters;\n};\n\nstruct rc_monitor_pack_t\n{\n    uint8_t channel_num;\n    uint16_t channel_val[0];\n};\n\nstruct AVFormatInternal\n{\n};\n\nstruct debug_osd_info\n{\n    debug_osd_item_t items[15];\n    int n_items;\n};\n\nstruct AVOptionRanges\n{\n    struct AVOptionRange **range;\n    int nb_ranges;\n    int nb_components;\n};\n\nstruct snd_pcm_sync_ptr\n{\n};\n\nunion anon_union_conflictac8f4_for_default_val\n{\n    int64_t i64;\n    double dbl;\n    char *str;\n    struct AVRational q;\n};\n\nstruct gs_playback_listener\n{\n    void *ctx;\n    int (*event_cb)(void *, playback_event_t);\n};\n\nstruct gs_local_video_info\n{\n    char video_path[256];\n    char audio_path[256];\n};\n\nunion anon_union_conflict3b31_for_field_1\n{\n    _Bool plugin;\n    uint8_t cam_workmode;\n    uint8_t cam_pbmode;\n    uint8_t cam_model;\n    _Bool wl_link;\n    gs_main_channel_id_t chnl_id;\n    gs_video_channel_id_t sub_chnl_id;\n    gs_local_video_info_t video_info;\n    uint32_t video_index;\n    _Bool is_pal;\n    gs_local_panorama_info_t pano_info;\n};\n\nstruct gs_video_channel_message\n{\n    enum gs_video_channel_message_id_t msg_id;\n    union anon_union_conflict3b31_for_field_1 field_1;\n};\n\nstruct snd_pcm_mmap_status\n{\n};\n\nstruct audio_dec\n{\n    void *gs_info;\n    void *cb_ctx;\n    int (*cb_func)(void *, dec_event_t);\n    audio_pb_state_t audio_pb_state;\n    int codec_id;\n    int sample_rate;\n    int channels;\n    int sample_fmt;\n    pl_decoder_handle_t hnd;\n    struct pl_decoder_itf_t *itf;\n    _Bool b_running;\n    _Bool b_eos;\n    undefined field12_0x2a;\n    undefined field13_0x2b;\n    struct duss_osal_task_handle_t *thread_dec;\n    _Bool b_aout_running;\n    undefined field16_0x31;\n    undefined field17_0x32;\n    undefined field18_0x33;\n    struct duss_osal_task_handle_t *thread_aout;\n    audio_resampler_ptr resampler;\n    uint8_t *resample_buffer;\n    int resample_buf_size;\n    int frames_received;\n    int frames_decoded;\n    FILE *f_a_out;\n    struct duss_osal_mutex_handle_t *list_mutex;\n    struct duss_list_head frame_list;\n    int num_in_list;\n    struct duss_osal_mutex_handle_t *pcm_mutex;\n    struct duss_list_head pcm_list;\n    int num_pcm;\n    _Bool b_started;\n    undefined field33_0x71;\n    undefined field34_0x72;\n    undefined field35_0x73;\n    struct timespec tstamp;\n    int samples_played;\n    _Bool b_first_frm_sent;\n    undefined field39_0x81;\n    undefined field40_0x82;\n    undefined field41_0x83;\n    undefined field42_0x84;\n    undefined field43_0x85;\n    undefined field44_0x86;\n    undefined field45_0x87;\n    int64_t play_tm_offset_ms;\n    int64_t play_tm_ms;\n};\n\nstruct AVCodecDefault\n{\n};\n\nstruct AVCodecTag\n{\n};\n\nstruct AVProgram\n{\n    int id;\n    int flags;\n    enum AVDiscard discard;\n    uint *stream_index;\n    uint nb_stream_indexes;\n    struct AVDictionary *metadata;\n    int program_num;\n    int pmt_pid;\n    int pcr_pid;\n    undefined field9_0x24;\n    undefined field10_0x25;\n    undefined field11_0x26;\n    undefined field12_0x27;\n    int64_t start_time;\n    int64_t end_time;\n    int64_t pts_wrap_reference;\n    int pts_wrap_behavior;\n    undefined field17_0x44;\n    undefined field18_0x45;\n    undefined field19_0x46;\n    undefined field20_0x47;\n};\n\nstruct AVInputFormat\n{\n    char *name;\n    char *long_name;\n    int flags;\n    char *extensions;\n    struct AVCodecTag **codec_tag;\n    struct AVClass *priv_class;\n    char *mime_type;\n    struct AVInputFormat *next;\n    int raw_codec_id;\n    int priv_data_size;\n    int (*read_probe)(struct AVProbeData *);\n    int (*read_header)(struct AVFormatContext *);\n    int (*read_packet)(struct AVFormatContext *, struct AVPacket *);\n    int (*read_close)(struct AVFormatContext *);\n    int (*read_seek)(struct AVFormatContext *, int, int64_t, int);\n    int64_t (*read_timestamp)(struct AVFormatContext *, int, int64_t *, int64_t);\n    int (*read_play)(struct AVFormatContext *);\n    int (*read_pause)(struct AVFormatContext *);\n    int (*read_seek2)(struct AVFormatContext *, int, int64_t, int64_t, int64_t, int);\n    int (*get_device_list)(struct AVFormatContext *, struct AVDeviceInfoList *);\n    int (*create_device_capabilities)(struct AVFormatContext *, struct AVDeviceCapabilitiesQuery *);\n    int (*free_device_capabilities)(struct AVFormatContext *, struct AVDeviceCapabilitiesQuery *);\n};\n\nstruct anon_struct_conflict69b3c_for_info\n{\n    int64_t last_dts;\n    int64_t duration_gcd;\n    int duration_count;\n    undefined field3_0x14;\n    undefined field4_0x15;\n    undefined field5_0x16;\n    undefined field6_0x17;\n    int64_t rfps_duration_sum;\n    double *duration_error[2][373];\n    undefined field9_0x24;\n    undefined field10_0x25;\n    undefined field11_0x26;\n    undefined field12_0x27;\n    int64_t codec_info_duration;\n    int64_t codec_info_duration_fields;\n    int found_decoder;\n    undefined field16_0x3c;\n    undefined field17_0x3d;\n    undefined field18_0x3e;\n    undefined field19_0x3f;\n    int64_t last_duration;\n    int64_t fps_first_dts;\n    int fps_first_dts_idx;\n    undefined field23_0x54;\n    undefined field24_0x55;\n    undefined field25_0x56;\n    undefined field26_0x57;\n    int64_t fps_last_dts;\n    int fps_last_dts_idx;\n    undefined field29_0x64;\n    undefined field30_0x65;\n    undefined field31_0x66;\n    undefined field32_0x67;\n};\n\nstruct duss_storage_client\n{\n    _Bool start_flag;\n    undefined field1_0x1;\n    undefined field2_0x2;\n    undefined field3_0x3;\n    struct duss_hal_storage_params param;\n    struct duss_hal_storage_info info;\n};\n\nstruct duss_osal_timer_handle_t\n{\n    struct duss_osal_timer_attrib_t attrib;\n    pthread_t thread;\n    int32_t repeat_cnt;\n    _Bool suspend;\n    _Bool restart;\n    undefined field5_0x22;\n    undefined field6_0x23;\n    struct sem_t sema;\n    struct sem_t lock;\n    struct timespec saved_time;\n};\n\nstruct rc_set_warning_mode_pack_t\n{\n    uint8_t ext_flag : 1;\n    uint8_t warning_tid : 4;\n    uint8_t warning_mode : 3;\n    uint16_t ext_val;\n};\n\nstruct AVClass\n{\n    char *class_name;\n    char *(*item_name)(void *);\n    struct AVOption *option;\n    int version;\n    int log_level_offset_offset;\n    int parent_log_context_offset;\n    void *(*child_next)(void *, void *);\n    AVClass *(*child_class_next)(struct AVClass *);\n    enum AVClassCategory category;\n    AVClassCategory (*get_category)(void *);\n    int (*query_ranges)(struct AVOptionRanges **, void *, char *, int);\n};\n\nstruct AVIOInterruptCB\n{\n    int (*callback)(void *);\n    void *opaque;\n};\n\nstruct AVPacketSideData\n{\n    uint8_t *data;\n    int size;\n    enum AVPacketSideDataType type;\n};\n\nstruct product_shm_info\n{\n    uint16_t frm_width;\n    uint16_t frm_height;\n    uint8_t fps;\n    uint8_t enc_strategy;\n    uint16_t lcdc_underflow_cnt;\n    uint32_t enc_sto_frm_dropped;\n    uint32_t enc_lv_frm_dropped;\n    uint32_t mipi_csi_frm_dropped;\n    uint32_t display_frm_dropped;\n    uint64_t audio_pts;\n    uint32_t local_fps_num;\n    uint32_t local_fps_den;\n    uint16_t frame_delay_e2e;\n    uint16_t cam_frame_interval;\n    uint16_t outliner_frame_interval;\n    uint16_t outliner_frame_interval_cnt;\n    uint16_t max_frame_delay_e2e;\n    uint16_t min_frame_delay_e2e;\n    uint16_t avg_frame_delay_e2e;\n    uint16_t if_switch;\n    uint8_t if_change_pipe;\n    uint8_t if_pb_pause;\n    uint8_t liveview_pipeline_running;\n    uint8_t avIn_pipeline_running;\n    uint8_t avIn_stream_type;\n    uint8_t pb_flush;\n    uint8_t disp_pannel_need_reset;\n    undefined field27_0x3f;\n};\n\nstruct debug_temp_osd_info\n{\n    debug_osd_item_t items[11];\n    int n_items;\n};\n\nstruct pl_muxer_itf_t\n{\n    int (*create)(p1_muxer_handle_t *, void *, void *);\n    int (*destroy)(p1_muxer_handle_t);\n    int (*prepare)(p1_muxer_handle_t);\n    int (*release)(p1_muxer_handle_t);\n    int (*add_stream)(p1_muxer_handle_t, enum AVCodecID, void *);\n    int (*write)(p1_muxer_handle_t, void *, uint32_t, int);\n    void *(*get_stream)(p1_muxer_handle_t, uint);\n    int (*config_stream)(p1_muxer_handle_t, enum pl_muxer_config_index_t, void *);\n};\n\nstruct AVFrame\n{\n    uint8_t *data[8];\n    int linesize[8];\n    uint8_t **extended_data;\n    int width;\n    int height;\n    int nb_samples;\n    int format;\n    int key_frame;\n    enum AVPictureType pict_type;\n    uint8_t *base[8];\n    struct AVRational sample_aspect_ratio;\n    undefined field11_0x84;\n    undefined field12_0x85;\n    undefined field13_0x86;\n    undefined field14_0x87;\n    int64_t pts;\n    int64_t pkt_pts;\n    int64_t pkt_dts;\n    int coded_picture_number;\n    int display_picture_number;\n    int quality;\n    int reference;\n    int8_t *qscale_table;\n    int qstride;\n    int qscale_type;\n    uint8_t *mbskip_table;\n    int16_t *motion_val[2];\n    uint32_t *mb_type;\n    short *dct_coeff;\n    int8_t *ref_index[2];\n    void *opaque;\n    undefined field31_0xdc;\n    undefined field32_0xdd;\n    undefined field33_0xde;\n    undefined field34_0xdf;\n    uint64_t error[8];\n    int type;\n    int repeat_pict;\n    int interlaced_frame;\n    int top_field_first;\n    int palette_has_changed;\n    int buffer_hints;\n    struct AVPanScan *pan_scan;\n    undefined field43_0x13c;\n    undefined field44_0x13d;\n    undefined field45_0x13e;\n    undefined field46_0x13f;\n    int64_t reordered_opaque;\n    void *hwaccel_picture_private;\n    struct AVCodecContext *owner;\n    void *thread_opaque;\n    uint8_t motion_subsample_log2;\n    undefined field52_0x155;\n    undefined field53_0x156;\n    undefined field54_0x157;\n    int sample_rate;\n    undefined field56_0x15c;\n    undefined field57_0x15d;\n    undefined field58_0x15e;\n    undefined field59_0x15f;\n    uint64_t channel_layout;\n    struct AVBufferRef *buf[8];\n    struct AVBufferRef **extended_buf;\n    int nb_extended_buf;\n    struct AVFrameSideData **side_data;\n    int nb_side_data;\n    int flags;\n    enum AVColorRange color_range;\n    enum AVColorPrimaries color_primaries;\n    enum AVColorTransferCharacteristic color_trc;\n    enum AVColorSpace colorspace;\n    enum AVChromaLocation chroma_location;\n    int64_t best_effort_timestamp;\n    int64_t pkt_pos;\n    int64_t pkt_duration;\n    struct AVDictionary *metadata;\n    int decode_error_flags;\n    int channels;\n    int pkt_size;\n    struct AVBufferRef *qp_table_buf;\n    undefined field80_0x1dc;\n    undefined field81_0x1dd;\n    undefined field82_0x1de;\n    undefined field83_0x1df;\n};\n\nstruct vdec_local_player\n{\n    gs_playback_listener_t pb_listener;\n    void *gs_info;\n    uint32_t state;\n    uint32_t cur_time;\n    _Bool b_pending_seek;\n    undefined field5_0x15;\n    undefined field6_0x16;\n    undefined field7_0x17;\n    uint32_t seek_pos;\n    struct AVFormatContext *fmt_ctx;\n    _Bool b_f_eof;\n    undefined field11_0x21;\n    undefined field12_0x22;\n    undefined field13_0x23;\n    int audio_stream_index;\n    audio_dec_t *adec;\n    _Bool b_a_eos;\n    undefined field17_0x2d;\n    undefined field18_0x2e;\n    undefined field19_0x2f;\n    int video_stream_index;\n    cp_vdec_t *vdec;\n    _Bool b_v_eos;\n    _Bool b_running;\n    undefined field24_0x3a;\n    undefined field25_0x3b;\n    struct duss_osal_task_handle_t *task_player;\n    struct duss_osal_mutex_handle_t *vmutex;\n    vdec_video_file_info_t file_info;\n    int64_t first_pts;\n};\n\nstruct AVDeviceInfoList\n{\n};\n\nstruct AVCodecDescriptor\n{\n    enum AVCodecID id;\n    enum AVMediaType type;\n    char *name;\n    char *long_name;\n    int props;\n    char **mime_types;\n};\n\nstruct AVFormatContext\n{\n    struct AVClass *av_class;\n    struct AVInputFormat *iformat;\n    struct AVOutputFormat *oformat;\n    void *priv_data;\n    struct AVIOContext *pb;\n    int ctx_flags;\n    uint nb_streams;\n    struct AVStream **streams;\n    char filename[1024];\n    int64_t start_time;\n    int64_t duration;\n    int bit_rate;\n    uint packet_size;\n    int max_delay;\n    int flags;\n    uint probesize;\n    int max_analyze_duration;\n    uint8_t *key;\n    int keylen;\n    uint nb_programs;\n    struct AVProgram **programs;\n    enum AVCodecID video_codec_id;\n    enum AVCodecID audio_codec_id;\n    enum AVCodecID subtitle_codec_id;\n    uint max_index_size;\n    uint max_picture_buffer;\n    uint nb_chapters;\n    struct AVChapter **chapters;\n    struct AVDictionary *metadata;\n    int64_t start_time_realtime;\n    int fps_probe_size;\n    int error_recognition;\n    struct AVIOInterruptCB interrupt_callback;\n    int debug;\n    undefined field34_0x494;\n    undefined field35_0x495;\n    undefined field36_0x496;\n    undefined field37_0x497;\n    int64_t max_interleave_delta;\n    int strict_std_compliance;\n    int event_flags;\n    int max_ts_probe;\n    int avoid_negative_ts;\n    int ts_id;\n    int audio_preload;\n    int max_chunk_duration;\n    int max_chunk_size;\n    int use_wallclock_as_timestamps;\n    int avio_flags;\n    enum AVDurationEstimationMethod duration_estimation_method;\n    undefined field50_0x4cc;\n    undefined field51_0x4cd;\n    undefined field52_0x4ce;\n    undefined field53_0x4cf;\n    int64_t skip_initial_bytes;\n    uint correct_ts_overflow;\n    int seek2any;\n    int flush_packets;\n    int probe_score;\n    int format_probesize;\n    char *codec_whitelist;\n    char *format_whitelist;\n    struct AVPacketList *packet_buffer;\n    struct AVPacketList *packet_buffer_end;\n    undefined field64_0x4fc;\n    undefined field65_0x4fd;\n    undefined field66_0x4fe;\n    undefined field67_0x4ff;\n    int64_t data_offset;\n    struct AVPacketList *raw_packet_buffer;\n    struct AVPacketList *raw_packet_buffer_end;\n    struct AVPacketList *parse_queue;\n    struct AVPacketList *parse_queue_end;\n    int raw_packet_buffer_remaining_size;\n    undefined field74_0x51c;\n    undefined field75_0x51d;\n    undefined field76_0x51e;\n    undefined field77_0x51f;\n    int64_t offset;\n    struct AVRational offset_timebase;\n    struct AVFormatInternal *internal;\n    int io_repositioned;\n    struct AVCodec *video_codec;\n    struct AVCodec *audio_codec;\n    struct AVCodec *subtitle_codec;\n    int metadata_header_padding;\n    void *opaque;\n    int (*control_message_cb)(struct AVFormatContext *, int, void *, size_t);\n    int64_t output_ts_offset;\n    int64_t max_analyze_duration2;\n    int64_t probesize2;\n    uint8_t *dump_separator;\n    undefined field92_0x56c;\n    undefined field93_0x56d;\n    undefined field94_0x56e;\n    undefined field95_0x56f;\n};\n\nstruct duss_event_client\n{\n    uint16_t this_host;\n    undefined field1_0x2;\n    undefined field2_0x3;\n    duss_mb_client_handle_t mb_client_obj;\n    struct duss_osal_msgq_handle_t *msgq;\n    struct duss_osal_task_handle_t *task;\n    struct duss_osal_mutex_handle_t *wait_ack_mutex;\n    struct duss_osal_event_handle_t *wait_ack_event;\n    duss_event_ack_identify_t wait_ack_list[32];\n    uint32_t wait_ack_cnt;\n    _Bool finish;\n    undefined field11_0x29d;\n    undefined field12_0x29e;\n    undefined field13_0x29f;\n    duss_event_cmd_desc_t *cmd_desc_tbl[79];\n    uint32_t cmd_desc_num[79];\n    void *userdata;\n};\n\nstruct MpegEncContext\n{\n};\n\nstruct AVPacketList\n{\n    struct AVPacket pkt;\n    struct AVPacketList *next;\n    undefined field2_0x5c;\n    undefined field3_0x5d;\n    undefined field4_0x5e;\n    undefined field5_0x5f;\n};\n\nstruct AVProbeData\n{\n    char *filename;\n    uchar *buf;\n    int buf_size;\n    char *mime_type;\n};\n\nstruct AVStream\n{\n    int index;\n    int id;\n    struct AVCodecContext *codec;\n    void *priv_data;\n    struct AVFrac pts;\n    struct AVRational time_base;\n    int64_t start_time;\n    int64_t duration;\n    int64_t nb_frames;\n    int disposition;\n    enum AVDiscard discard;\n    struct AVRational sample_aspect_ratio;\n    struct AVDictionary *metadata;\n    struct AVRational avg_frame_rate;\n    undefined field14_0x64;\n    undefined field15_0x65;\n    undefined field16_0x66;\n    undefined field17_0x67;\n    struct AVPacket attached_pic;\n    struct AVPacketSideData *side_data;\n    int nb_side_data;\n    int event_flags;\n    struct anon_struct_conflict69b3c_for_info *info;\n    int pts_wrap_bits;\n    undefined field24_0xd4;\n    undefined field25_0xd5;\n    undefined field26_0xd6;\n    undefined field27_0xd7;\n    int64_t first_dts;\n    int64_t cur_dts;\n    int64_t last_IP_pts;\n    int last_IP_duration;\n    int probe_packets;\n    int codec_info_nb_frames;\n    enum AVStreamParseType need_parsing;\n    struct AVCodecParserContext *parser;\n    struct AVPacketList *last_in_packet_buffer;\n    struct AVProbeData probe_data;\n    int64_t pts_buffer[17];\n    struct AVIndexEntry *index_entries;\n    int nb_index_entries;\n    uint index_entries_allocated_size;\n    struct AVRational r_frame_rate;\n    int stream_identifier;\n    int64_t interleaver_chunk_size;\n    int64_t interleaver_chunk_duration;\n    int request_probe;\n    int skip_to_keyframe;\n    int skip_samples;\n    undefined field49_0x1d4;\n    undefined field50_0x1d5;\n    undefined field51_0x1d6;\n    undefined field52_0x1d7;\n    int64_t first_discard_sample;\n    int64_t last_discard_sample;\n    int nb_decoded_frames;\n    undefined field56_0x1ec;\n    undefined field57_0x1ed;\n    undefined field58_0x1ee;\n    undefined field59_0x1ef;\n    int64_t mux_ts_offset;\n    int64_t pts_wrap_reference;\n    int pts_wrap_behavior;\n    int update_initial_durations_done;\n    int64_t pts_reorder_error[17];\n    uint8_t pts_reorder_error_count[17];\n    undefined field66_0x2a1;\n    undefined field67_0x2a2;\n    undefined field68_0x2a3;\n    undefined field69_0x2a4;\n    undefined field70_0x2a5;\n    undefined field71_0x2a6;\n    undefined field72_0x2a7;\n    int64_t last_dts_for_order_check;\n    uint8_t dts_ordered;\n    uint8_t dts_misordered;\n    undefined field76_0x2b2;\n    undefined field77_0x2b3;\n    int inject_global_side_data;\n    char *recommended_encoder_configuration;\n    struct AVRational display_aspect_ratio;\n    undefined field81_0x2c4;\n    undefined field82_0x2c5;\n    undefined field83_0x2c6;\n    undefined field84_0x2c7;\n};\n\nstruct duss_hal_mem_buf\n{\n};\n\nstruct AVSubtitleRect\n{\n    int x;\n    int y;\n    int w;\n    int h;\n    int nb_colors;\n    struct AVPicture pict;\n    enum AVSubtitleType type;\n    char *text;\n    char *ass;\n    int flags;\n};\n\nstruct AVIOContext\n{\n    struct AVClass *av_class;\n    uchar *buffer;\n    int buffer_size;\n    uchar *buf_ptr;\n    uchar *buf_end;\n    void *opaque;\n    int (*read_packet)(void *, uint8_t *, int);\n    int (*write_packet)(void *, uint8_t *, int);\n    int64_t (*seek)(void *, int64_t, int);\n    undefined field9_0x24;\n    undefined field10_0x25;\n    undefined field11_0x26;\n    undefined field12_0x27;\n    int64_t pos;\n    int must_flush;\n    int eof_reached;\n    int write_flag;\n    int max_packet_size;\n    ulong checksum;\n    uchar *checksum_ptr;\n    ulong (*update_checksum)(ulong, uint8_t *, uint);\n    int error;\n    int (*read_pause)(void *, int);\n    int64_t (*read_seek)(void *, int, int64_t, int);\n    int seekable;\n    undefined field25_0x5c;\n    undefined field26_0x5d;\n    undefined field27_0x5e;\n    undefined field28_0x5f;\n    int64_t maxsize;\n    int direct;\n    undefined field31_0x6c;\n    undefined field32_0x6d;\n    undefined field33_0x6e;\n    undefined field34_0x6f;\n    int64_t bytes_read;\n    int seek_count;\n    int writeout_count;\n    int orig_buffer_size;\n    undefined field39_0x84;\n    undefined field40_0x85;\n    undefined field41_0x86;\n    undefined field42_0x87;\n};\n\nstruct AVOption\n{\n    char *name;\n    char *help;\n    int offset;\n    enum AVOptionType type;\n    union anon_union_conflictac8f4_for_default_val default_val;\n    double min;\n    double max;\n    int flags;\n    char *unit;\n};\n\nstruct AVSubtitle\n{\n    uint16_t format;\n    undefined field1_0x2;\n    undefined field2_0x3;\n    uint32_t start_display_time;\n    uint32_t end_display_time;\n    uint num_rects;\n    struct AVSubtitleRect **rects;\n    undefined field7_0x14;\n    undefined field8_0x15;\n    undefined field9_0x16;\n    undefined field10_0x17;\n    int64_t pts;\n};\n\nstruct duss_osal_task_handle_t\n{\n    struct duss_osal_task_attrib_t attrib;\n    pthread_t thread;\n};\n\nstruct modem_shmem_info_t\n{\n    uint32_t frm_idx;\n    uint16_t frm_isI;\n    uint16_t frm_len;\n    uint16_t frm_dsti;\n    uint16_t frm_dstf;\n    uint16_t channel_status;\n    uint16_t dec_err_status;\n    uint16_t cur_time;\n    uint16_t delta_time;\n    uint16_t dbg_msc;\n    uint8_t dbg_ap_ready;\n    uint8_t dbg_cp_ready;\n    uint32_t local_id;\n    uint8_t cp_state;\n    uint8_t cp_report;\n    uint8_t cp_report_seq;\n    uint8_t client_type;\n    uint32_t cp_boot_status0;\n    uint32_t cp_boot_status1;\n    uint32_t board_version;\n    uint32_t board_sub_version;\n    uint8_t machine_role;\n    uint8_t is_reverse;\n    int8_t cp_tx_power;\n    uint8_t gnd_type;\n    uint32_t cp_sssfn;\n    uint32_t ulow_en;\n    uint8_t mipi_rx_response;\n    uint8_t liveview_broken_status;\n    uint8_t reserved01;\n    uint8_t reserved02;\n    uint8_t ap_reboot_flag;\n    uint8_t ap_reboot_ack_flag;\n    uint8_t secure_sync_flag;\n    uint8_t reserve00;\n    uint8_t area_state;\n    uint8_t area_substate;\n    uint8_t GsCtrl;\n    uint8_t GsSubState;\n    uint8_t WaterLevel[4];\n    uint16_t RxCntStastic[4];\n    uint16_t TxCntStastic[4];\n    uint8_t Reserved[8];\n    uint32_t com_uart_status;\n    uint32_t fcr_rx_status;\n    uint32_t fcr_tx_status;\n    uint32_t frm_idx_for_display;\n    uint32_t frm_delay_for_display;\n    uint16_t wifi_sdr_mode;\n    uint16_t frm_isI_for_display;\n    uint32_t country_code;\n    uint16_t frm_len_for_display;\n    uint16_t delta_time_for_display;\n    uint8_t uint8_t_reboot_reason;\n    undefined field54_0x85;\n    undefined field55_0x86;\n    undefined field56_0x87;\n    uint64_t cpa7_version;\n    uint64_t dsp_version;\n    uint8_t u8_dual_band_capability;\n    undefined field60_0x99;\n    undefined field61_0x9a;\n    undefined field62_0x9b;\n    undefined field63_0x9c;\n    undefined field64_0x9d;\n    undefined field65_0x9e;\n    undefined field66_0x9f;\n};\n\nstruct sqlite3\n{\n};\n\nstruct cp_vdec\n{\n    void *gs_info;\n    uint32_t width;\n    uint32_t height;\n    void *cb_ctx;\n    int (*cb_func)(void *, dec_event_t);\n    vdec_state_t vdec_state;\n    _Bool b_running;\n    _Bool b_eos;\n    _Bool b_first_frm_sent;\n    undefined field9_0x1b;\n    gs_media_cmd_chnl_t *mcc;\n    int dmi_data_fd;\n    struct duss_osal_task_handle_t *thread_tx;\n    int frames_received;\n    int frames_sent;\n    struct duss_osal_mutex_handle_t *list_mutex;\n    struct duss_list_head frame_list;\n    int num_in_list;\n    int64_t play_tm_ms;\n};\n\nstruct AVOutputFormat\n{\n    char *name;\n    char *long_name;\n    char *mime_type;\n    char *extensions;\n    enum AVCodecID audio_codec;\n    enum AVCodecID video_codec;\n    enum AVCodecID subtitle_codec;\n    int flags;\n    struct AVCodecTag **codec_tag;\n    struct AVClass *priv_class;\n    struct AVOutputFormat *next;\n    int priv_data_size;\n    int (*write_header)(struct AVFormatContext *);\n    int (*write_packet)(struct AVFormatContext *, struct AVPacket *);\n    int (*write_trailer)(struct AVFormatContext *);\n    int (*interleave_packet)(struct AVFormatContext *, struct AVPacket *, struct AVPacket *, int);\n    int (*query_codec)(enum AVCodecID, int);\n    void (*get_output_timestamp)(struct AVFormatContext *, int, int64_t *, int64_t *);\n    int (*control_message)(struct AVFormatContext *, int, void *, size_t);\n    int (*write_uncoded_frame)(struct AVFormatContext *, int, struct AVFrame **, uint);\n    int (*get_device_list)(struct AVFormatContext *, struct AVDeviceInfoList *);\n    int (*create_device_capabilities)(struct AVFormatContext *, struct AVDeviceCapabilitiesQuery *);\n    int (*free_device_capabilities)(struct AVFormatContext *, struct AVDeviceCapabilitiesQuery *);\n};\n\nstruct rc_set_function_pack_t\n{\n    uint8_t arm_channel : 2;\n    uint8_t arm_setval : 2;\n    uint8_t flip_channel : 2;\n    uint8_t flip_setval : 2;\n};\n\nstruct pl_muxer_t\n{\n    struct pl_muxer_itf_t *itf;\n    void *context;\n    void *me;\n};\n\nstruct AVCodec\n{\n    char *name;\n    char *long_name;\n    enum AVMediaType type;\n    enum AVCodecID id;\n    int capabilities;\n    struct AVRational *supported_framerates;\n    enum AVPixelFormat *pix_fmts;\n    int *supported_samplerates;\n    enum AVSampleFormat *sample_fmts;\n    uint64_t *channel_layouts;\n    uint8_t max_lowres;\n    undefined field11_0x29;\n    undefined field12_0x2a;\n    undefined field13_0x2b;\n    struct AVClass *priv_class;\n    struct AVProfile *profiles;\n    int priv_data_size;\n    struct AVCodec *next;\n    int (*init_thread_copy)(struct AVCodecContext *);\n    int (*update_thread_context)(struct AVCodecContext *, struct AVCodecContext *);\n    struct AVCodecDefault *defaults;\n    void (*init_static_data)(struct AVCodec *);\n    int (*init)(struct AVCodecContext *);\n    int (*encode_sub)(struct AVCodecContext *, uint8_t *, int, struct AVSubtitle *);\n    int (*encode2)(struct AVCodecContext *, struct AVPacket *, struct AVFrame *, int *);\n    int (*decode)(struct AVCodecContext *, void *, int *, struct AVPacket *);\n    int (*close)(struct AVCodecContext *);\n    void (*flush)(struct AVCodecContext *);\n};\n\nstruct snd_pcm_mmap_control\n{\n};\n"
  },
  {
    "path": "jni/rec/rec_util.c",
    "content": "#include \"rec_util.h\"\n#include <string.h>\n\nvoid rec_util_osd_path_from_video_path(const char *video_path, char *osd_path, size_t osd_path_size)\n{\n    char *file_basename = strrchr(video_path, '/') + 1;\n    char *file_ext = strrchr(file_basename, '.');\n    uint8_t file_dir_len = file_basename - video_path - 1;\n    uint8_t file_stem_len = file_ext - file_basename;\n\n    snprintf(\n        osd_path,\n        osd_path_size,\n        \"%.*s/%.*s.osd\",\n        file_dir_len,\n        video_path,\n        file_stem_len,\n        file_basename);\n}\n"
  },
  {
    "path": "jni/rec/rec_util.h",
    "content": "#pragma once\n\n#include <stdlib.h>\n\nvoid rec_util_osd_path_from_video_path(const char *video_path, char *osd_path, size_t osd_path_size);\n"
  },
  {
    "path": "jni/toast/toast.c",
    "content": "#include <assert.h>\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <time.h>\n#include <ctype.h>\n#include \"json/osd_config.h\"\n#include \"util/time_util.h\"\n\n#define DATASIZE 20\n#define TOP_ROW 5\n#define BOTTOM_ROW 8\n\n#define TOAST_DISABLE_KEY \"hide_diagnostics\"\n\n#include <stdbool.h>\n\nint toast_enabled = 1;\ntypedef struct ToastItem\n{\n    char data[DATASIZE];\n    struct timespec shown;\n    struct ToastItem *nextNode;\n} ToastItem;\n\nToastItem *bottomStackPointer = NULL;\nToastItem *topStackPointer = NULL;\n\nstruct timespec now, lasttoastremoval;\n\nvoid toast_load_config()\n{\n    if (get_boolean_config_value(TOAST_DISABLE_KEY))\n    {\n        toast_enabled = 0;\n    }\n}\n\nint toast_pop()\n{\n    if (bottomStackPointer == NULL)\n        return 0;\n\n    char data[DATASIZE];\n    strcpy(data, bottomStackPointer->data);\n    bottomStackPointer->shown = (struct timespec){0, 0};\n    ToastItem *TempPointer = bottomStackPointer;\n    bottomStackPointer = bottomStackPointer->nextNode;\n    free(TempPointer);\n    if (bottomStackPointer == NULL)\n        topStackPointer = NULL;\n    return 1;\n}\n\nint toast(char *data, ...)\n{\n    if (!toast_enabled) {\n        return 0;\n    }\n    ToastItem *TempPointer = malloc(sizeof(ToastItem));\n    if (TempPointer == NULL)\n        return 0;\n\n    // printf\n    va_list va;\n    va_start(va, data);\n    char copy[DATASIZE];\n    vsnprintf(copy, DATASIZE, data, va);\n\n    // our fonts are caps only....\n    for (int i = 0; copy[i]; i++)\n    {\n        copy[i] = toupper(copy[i]);\n    }\n\n    strncpy(TempPointer->data, copy, DATASIZE);\n    TempPointer->shown = (struct timespec){0, 0};\n    TempPointer->nextNode = NULL;\n    if (bottomStackPointer == NULL)\n        bottomStackPointer = TempPointer;\n    else\n        topStackPointer->nextNode = TempPointer;\n    topStackPointer = TempPointer;\n    return 1;\n}\n\nvoid do_toast(void (*display_print_string)(uint8_t init_x, uint8_t y, const char *string, uint8_t len))\n{\n    if (!toast_enabled) {\n        return;\n    }\n\n    int numberoffNodes = 0;\n\n    clock_gettime(CLOCK_MONOTONIC, &now);\n\n    // if the last item has been up 3 seconds; chop it\n    // but only remove 1 per second\n    ToastItem *TempPointer = bottomStackPointer;\n    if (\n        timespec_subtract_ns(&now, &lasttoastremoval) > NSEC_PER_SEC                      // 1ce per second limit\n        && TempPointer != NULL && TempPointer->shown.tv_sec > 0                           // guards\n        && timespec_subtract_ns(&now, &TempPointer->shown) > ((uint64_t)NSEC_PER_SEC * 3) // item has been on screen for long enough\n    )\n    {\n        clock_gettime(CLOCK_MONOTONIC, &lasttoastremoval);\n        TempPointer = TempPointer->nextNode;\n        toast_pop();\n    }\n\n    // loop and display\n    for (int row = BOTTOM_ROW; row >= TOP_ROW; row--)\n    {\n        // if we ran out of items blank out the rest of our rows\n        if (TempPointer == NULL) {\n            char empty[DATASIZE] = {0};\n            display_print_string(0, row, empty, DATASIZE);\n        } else {\n\n            // set shown time if not set (ie: item not yet shown)\n            if (TempPointer->shown.tv_sec == 0)\n            {\n                clock_gettime(CLOCK_MONOTONIC, &TempPointer->shown);\n            }\n\n            // draw the current item in the current row\n            display_print_string(0, row, TempPointer->data, DATASIZE);\n            TempPointer = TempPointer->nextNode;\n        }\n    };\n\n    return;\n}\n"
  },
  {
    "path": "jni/toast/toast.h",
    "content": "#pragma once\n\nint toast(char*, ...);\nvoid toast_load_config();\nvoid do_toast(void (*display_print_string)(uint8_t init_x, uint8_t y, const char *string, uint8_t len));"
  },
  {
    "path": "jni/util/debug.h",
    "content": "// #pragma once\n\n#ifdef DEBUG\n#define DEBUG_PRINT(fmt, args...)    fprintf(stderr, fmt, ## args)\n#else\n#define DEBUG_PRINT(fmt, args...)\n#endif"
  },
  {
    "path": "jni/util/display_info.h",
    "content": "#pragma once\n\n#include <stdint.h>\n\n#define NUM_CHARS 256\n#define NUM_FONT_PAGES 4\n\ntypedef struct display_info_s {\n    uint8_t char_width;\n    uint8_t char_height;\n    uint8_t font_width;\n    uint8_t font_height;\n    uint16_t x_offset;\n    uint16_t y_offset;\n    void *fonts[NUM_FONT_PAGES];\n} display_info_t;"
  },
  {
    "path": "jni/util/fs_util.c",
    "content": "#include <stdint.h>\n#include <stdio.h>\n#include <fcntl.h>\n#include <unistd.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdlib.h>\n#include <sys/mman.h>\n#include <sys/stat.h>\n\n#include \"fs_util.h\"\n\n#define DICTIONARY_PATH \"/opt/mspdictionaries\"\n\nint32_t get_int_from_fs(char* path) {\n    int32_t val;\n    char read_buffer[32];\n    memset(read_buffer, 0, 32);\n    int fd = open(path, O_RDONLY, 0);\n    if(fd < 0) {\n        return -1;\n    }\n    int read_count = read(fd, read_buffer, 32);\n    if(read_count > 0) {\n        val = atoi(read_buffer);\n    }\n    close(fd);\n    return val;\n}\n\nvoid *open_dict(int dict_version, int *size) {\n    char file_path[255];\n    snprintf(file_path, 255, \"%s/dictionary_%d.bin\", DICTIONARY_PATH, dict_version);\n    struct stat st;\n    memset(&st, 0, sizeof(st));\n    stat(file_path, &st);\n    size_t filesize = st.st_size;\n    int fd = open(file_path, O_RDONLY, 0);\n    if (!fd) {\n        return NULL;\n    }\n    void* dict = malloc(filesize);\n    void* mmappedData = mmap(NULL, filesize, PROT_READ, MAP_PRIVATE, fd, 0);\n    *size = filesize;\n    memcpy(dict, mmappedData, filesize);\n    close(fd);\n    munmap(mmappedData, filesize);\n    return dict;\n}\n"
  },
  {
    "path": "jni/util/fs_util.h",
    "content": "#pragma once\n\n#include <stdint.h>\n\nint32_t get_int_from_fs(char*);\nvoid *open_dict(int, int *);"
  },
  {
    "path": "jni/util/time_util.h",
    "content": "#pragma once\n\n#include <time.h>\n#include <stdbool.h>\n\n#define NSEC_PER_SEC 1000000000\n#define MSEC_PER_SEC 1000\n\nstatic inline void timespec_subtract(struct timespec *res, const struct timespec *a, const struct timespec *b)\n{\n\tres->tv_sec = a->tv_sec - b->tv_sec;\n\tres->tv_nsec = a->tv_nsec - b->tv_nsec;\n\tif (res->tv_nsec < 0) {\n\t\tres->tv_sec--;\n\t\tres->tv_nsec += NSEC_PER_SEC;\n\t}\n}\n\nstatic inline int64_t timespec_subtract_ns(const struct timespec *a, const struct timespec *b)\n{\n\tstruct timespec res;\n\ttimespec_subtract(&res, a, b);\n\treturn (int64_t)res.tv_sec * NSEC_PER_SEC + res.tv_nsec;\n}"
  },
  {
    "path": "libshims/duml_hal.c",
    "content": "#include \"jni/hw/duml_hal.h\"\n\nduss_result_t duss_hal_initialize(duss_hal_device_desc_t *)\n{\n    duss_result_t dummy;\n\n    return dummy;\n}\n\n\nduss_result_t duss_hal_deinitialize()\n{\n    duss_result_t dummy;\n\n    return dummy;\n}\n\n\nduss_result_t duss_hal_device_open(char *device_name, void *unknown, duss_hal_obj_handle_t *)\n{\n    duss_result_t dummy;\n\n    return dummy;\n}\n\n\nduss_result_t duss_hal_device_start(duss_hal_obj_handle_t, void *)\n{\n    duss_result_t dummy;\n\n    return dummy;\n}\n\n\nduss_result_t duss_hal_device_close(duss_hal_obj_handle_t)\n{\n    duss_result_t dummy;\n\n    return dummy;\n}\n\n\nduss_result_t duss_hal_device_stop(duss_hal_obj_handle_t)\n{\n    duss_result_t dummy;\n\n    return dummy;\n}\n\n\nduss_result_t duss_hal_mem_alloc(duss_hal_obj_handle_t, duss_hal_mem_handle_t *, uint32_t size, uint32_t, uint32_t, uint32_t)\n{\n    duss_result_t dummy;\n\n    return dummy;\n}\n\n\nduss_result_t duss_hal_mem_get_phys_addr(duss_hal_mem_handle_t, void **)\n{\n    duss_result_t dummy;\n\n    return dummy;\n}\n\n\nduss_result_t duss_hal_mem_map(duss_hal_mem_handle_t, void **)\n{\n    duss_result_t dummy;\n\n    return dummy;\n}\n\n\nduss_result_t duss_hal_mem_free(duss_hal_mem_handle_t)\n{\n    duss_result_t dummy;\n\n    return dummy;\n}\n\n\nduss_result_t duss_hal_mem_sync(duss_hal_mem_handle_t, uint32_t)\n{\n    duss_result_t dummy;\n\n    return dummy;\n}\n\n\nduss_result_t duss_hal_display_open(duss_hal_obj_handle_t, duss_disp_instance_handle_t **, duss_disp_vop_id_t)\n{\n    duss_result_t dummy;\n\n    return dummy;\n}\n\n\nduss_result_t duss_hal_display_close(duss_hal_obj_handle_t, duss_disp_instance_handle_t **)\n{\n    duss_result_t dummy;\n\n    return dummy;\n}\n\n\nduss_result_t duss_hal_display_aquire_plane(duss_disp_instance_handle_t *, duss_disp_plane_type_t, duss_disp_plane_id_t *)\n{\n    duss_result_t dummy;\n\n    return dummy;\n}\n\n\nduss_result_t duss_hal_display_reset(duss_disp_instance_handle_t *)\n{\n    duss_result_t dummy;\n\n    return dummy;\n}\n\n\nduss_result_t duss_hal_display_register_frame_cycle_callback(duss_disp_instance_handle_t *, duss_disp_plane_id_t, frame_pop_handler *, void *)\n{\n    duss_result_t dummy;\n\n    return dummy;\n}\n\n\nduss_result_t duss_hal_display_timing_detail_get(duss_disp_instance_handle_t *, duss_disp_timing_detail_t *)\n{\n    duss_result_t dummy;\n\n    return dummy;\n}\n\n\nduss_result_t duss_hal_display_port_enable(duss_disp_instance_handle_t *, duss_disp_port_id_t, uint8_t)\n{\n    duss_result_t dummy;\n\n    return dummy;\n}\n\n\nduss_result_t duss_hal_display_plane_blending_set(duss_disp_instance_handle_t *, duss_disp_plane_id_t, duss_disp_plane_blending_t *)\n{\n    duss_result_t dummy;\n\n    return dummy;\n}\n\n\nduss_result_t duss_hal_display_release_plane(duss_disp_instance_handle_t *, duss_disp_plane_id_t)\n{\n    duss_result_t dummy;\n\n    return dummy;\n}\n\n\nduss_result_t duss_hal_display_push_frame(duss_disp_instance_handle_t *, duss_disp_plane_id_t, duss_frame_buffer_t *)\n{\n    duss_result_t dummy;\n\n    return dummy;\n}\n\n\nduss_result_t duss_hal_attach_disp(char *param_1, duss_hal_obj **param_2)\n{\n    duss_result_t dummy;\n\n    return dummy;\n}\n\n\nduss_result_t duss_hal_attach_ion_mem(char *param_1, duss_hal_obj **param_2)\n{\n    duss_result_t dummy;\n\n    return dummy;\n}\n\n\nduss_result_t duss_hal_detach_ion_mem()\n{\n    duss_result_t dummy;\n\n    return dummy;\n}\n\n\nduss_result_t duss_hal_detach_disp()\n{\n    duss_result_t dummy;\n\n    return dummy;\n}\n\n\n"
  }
]